Tabla de contenido

Visualización interactiva de datos geográficos

Objetivos

  • Utilizar mapas coropléticos para representar datos en distintas regiones geográficas

  • Generar mapas coropléticos interactivos, incluyendo mapas que representan países en el mundo y mapas que representan estados en EE.UU., haciendo cambios de diseño para añadir funcionalidad/estética, y añadiendo animaciones

  • Generar gráficos de dispersión interactivos en los mapas, incluidos los gráficos de dispersión que indican las geolocalizaciones de lugares de interés y gráficos de burbujas interactivos en mapas

  • Generar gráficos lineales interactivos en mapas, incluyendo aquellos que indiquen trayectorias en un mapa

En este capítulo, aprenderemos a utilizar visualizaciones interactivas para representar datos en regiones geográficas.

Introducción

  • La mayoría de los conjuntos de datos generados en el mundo actual incluyen algunas características que representan aspectos espaciales o aspectos geográficos. Por ejemplo, los usuarios de las plataformas de medios sociales se caracterizan por las diferentes partes del mundo en las que residen, las métricas de desarrollo mundial se se calculan para diferentes países del mundo, las rutas de transporte que abarcan diferentes lugares del planeta, etc. Es esencial aprender aprender formas sistemáticas de entender y presentar esa información de forma digerible y a la vez perspicaz.

  • Esta sección le ayudará a desarrollar esta capacidad proporcionando las herramientas necesarias para generar una variedad de gráficos que representen datos geográficos. Mientras que altair y geopandas ofrecen interesantes posibilidades para visualizar datos geográficos datos geográficos, plotly es especialmente bueno para generar una variedad de gráficos geográficos que son fáciles de construir, depurar y personalizar. Por lo tanto, en este capítulo, utilizaremos plotly para demostrar la generación de diferentes clases de gráficos geográficos con múltiples conjuntos de datos disponibles públicamente de una variedad de contextos.

Mapas coropléticos

  • Un mapa coroplético es un mapa de una región con diferentes divisiones coloreadas para indicar el valor de una característica específica en esa división. Esta división puede ser un país, un estado, un distrito o cualquier otra zona bien documentada. Por ejemplo, se puede visualizar la población de un país en un mapa mundial, la población de un estado en un mapa de un país o el porcentaje de población con acceso a una tecnología con un mapa coropléjico.

Ejercicio 45: Creación de un mapa coroplético

  • En la primera visualización de esta sección, vamos a utilizar las estadísticas de uso de Internet publicadas en Our World in Data (https://ourworldindata.org/internet) y presentar el porcentaje de la población que utiliza Internet en cada país desde 1990 hasta 2017. ¿Se ha fijado en la característica llamada Código en el conjunto de datos? Se trata de un código asignado a cada país por una norma llamada ISO 3166-1. Se utiliza ampliamente para que los desarrolladores de todo el mundo tengan una forma común de referirse y acceder a los nombres de los países en cualquier dato.

  • En este ejercicio, generaremos un mapa coroplético mundial utilizando el conjunto de datos Our World in Datos. Como el DataFrame contiene registros de varios años, primero vamos a subdividir los datos en un año específico, por ejemplo, 2016. A continuación, utilizaremos este subconjunto para generar un mapa mundial. Para ello, vamos a seguir los siguientes pasos

  • Importar los módulos de Python

import pandas as pd
  • Lea los datos del archivo .csv

internet_usage_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/share-of-individuals-using-the-internet.csv"
internet_usage_df = pd.read_csv(internet_usage_url)
  • Hacer sub-conjuntos de datos de un año específico, ya que el DataFrame contiene registros de múltiples años

internet_usage_2016 = internet_usage_df.query("Year==2016")
internet_usage_2016.head()
Country Code Year Individuals using the Internet (% of population)
16 Afghanistan AFG 2016 10.595726
39 Albania ALB 2016 66.363445
63 Algeria DZA 2016 42.945527
85 Andorra AND 2016 97.930637
107 Angola AGO 2016 13.000000
  • Para los siguientes pasos, vamos a utilizar el módulo express (por su simplicidad) de plotly y utilizar la función choropleth del módulo. El primer argumento que se pasa a esta función es el DataFrame que queremos visualizar. Los siguientes parámetros:

    • locations: Se establece como el nombre de la columna del DataFrame que contiene los códigos de país ISO 3166

    • color: Se establece el nombre de la columna que contiene la característica numérica con la que se va a colorear el mapa

    • hover_name: Se establece el nombre de la columna que contiene la característica que se mostrará al pasar el ratón por encima del mapa

    • color_continuous_scale: Esto se establece en un esquema de color, como: Blues | Reds | Greens | px.colors.sequential.Plasma.

  • Genera un mapa coroplético mundial interactivo utilizando la función choropleth de la biblioteca plotly

import plotly.express as px
from IPython.display import IFrame, display, HTML
fig = px.choropleth(internet_usage_2016, locations="Code", # colunm containing ISO 3166 country codes
                    color = "Individuals using the Internet (% of population)", # column by which to color-code
                    hover_name = "Country", # column to display in hover information
                    color_continuous_scale = px.colors.sequential.Plasma)
# fig.show(renderer="svg", width=900, height=410)
fig.write_html('figures/figure1.html', auto_open=False)
IFrame('figures/figure1.html', width=900, height=415)
# display(HTML('figures/figure1.html'))
  • Observemos el gráfico con atención y veamos si las observaciones coinciden con nuestros conocimientos generales. Como era de esperar, el uso de Internet en el mundo occidental es mayor que en que en el este.

  • Es interesante ver en la Figura que un mayor porcentaje de la población (\(\approx91,6\)) en Australia y Canadá tiene acceso a Internet en comparación con Estados Unidos y la mayoría de los países europeos (\(\approx\)59,5).

Ejercicio 46: Ajustar un mapa coroplético mundial

  • En este ejercicio, haremos algunos cambios sencillos en el diseño del mapa coropleto, como cambiar la proyección del mapa de flat a natural earth, hacer zoom en una región específica, añadir texto al mapa utilizando la función update_layout, y añadir una función de rotación.

  • Importar los módulos de Python

import pandas as pd
  • Lea los datos del archivo .csv

internet_usage_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/share-of-individuals-using-the-internet.csv"
internet_usage_df = pd.read_csv(internet_usage_url)
  • Hacer sub-conjuntos de datos de un año específico, ya que el DataFrame contiene registros de múltiples años

internet_usage_2016 = internet_usage_df.query("Year==2016")
internet_usage_2016.head()
Country Code Year Individuals using the Internet (% of population)
16 Afghanistan AFG 2016 10.595726
39 Albania ALB 2016 66.363445
63 Algeria DZA 2016 42.945527
85 Andorra AND 2016 97.930637
107 Angola AGO 2016 13.000000
  • Añade el texto del título al mapa coropleto estableciendo el parámetro title_text

import plotly.express as px

fig = px.choropleth(internet_usage_2016,
                    locations="Code",
                    color="Individuals using the Internet (% of population)", # column by which to color-code
                    hover_name="Country", # column to display in hover information 
                    color_continuous_scale=px.colors.sequential.Plasma)
fig.update_layout(
    # add a title text for the plot
    title_text = 'Internet usage across the world (% population)'
)
fig.show(renderer="svg")
_images/.~section2_26_0.svg
  • Digamos que sólo nos interesa ver el uso de Internet en el continente de Sur América. Establezca geo_scope a south america en la función update_layout para hacer zoom en la región de asia. Podemos hacerlo rápidamente con el siguiente código

import plotly.express as px

fig = px.choropleth(internet_usage_2016,
                    locations="Code",
                    color="Individuals using the Internet (% of population)", # column by which to color-code
                    hover_name="Country", # column to display in hover information 
                    color_continuous_scale=px.colors.sequential.Plasma)
fig.update_layout(
    # add a title text for the plot
    title_text = 'Uso de Internet en todo Sur América (% de población) - 2016',
    geo_scope = 'south america' # can be set to north america | south america | africa | asia | europe | usa
)
fig.show()
import plotly.express as px

fig = px.choropleth(internet_usage_2016,
                    locations="Code",
                    color="Individuals using the Internet (% of population)", # column by which to color-code
                    hover_name="Country", # column to display in hover information 
                    color_continuous_scale=px.colors.sequential.Plasma)
fig.update_layout(
    # add a title text for the plot
    title_text = 'Internet usage across the Asian Continent (% population) - 2016',
    geo_scope = 'asia' # can be set to north america | south america | africa | asia | europe | usa
)
fig.show()
  • Establece projection type como natural earth. La gráfica podría rotarse como un globo terráqueo real

import plotly.express as px

fig = px.choropleth(internet_usage_2016,
                    locations="Code",
                    color="Individuals using the Internet (% of population)", # column by which to color-code
                    hover_name="Country", # column to display in hover information
                    color_continuous_scale=px.colors.sequential.Plasma)

fig.update_layout(
    # add a title text for the plot
    title_text = 'Internet usage across the world (% population)',
    # set projection style for the plot
    geo = dict(projection={'type':'natural earth'}) # by default, projection type is set to 'equirectangular'
)

fig.show()
  • Prueba a arrastrar el mapa ahora. La rotación le da al gráfico un toque mucho más realista. plotly ofrece muchas opciones de este tipo para ajustar las visualizaciones. Para experimentar con otros estilos de proyección aparte de los que hemos visto en nuestros ejemplos, visite la documentación oficial de documentación de plotly aquí: https://plot.ly/python/reference/#layout-geo- proyección.

Ejercicio 47: Añadir animación a un mapa coroplético

  • En este ejercicio, animaremos un mapa coroplético mundial. Primero, elegiremos una columna. A continuación, añadiremos un componente deslizante al mapa para ver los registros en diferentes puntos temporales

  • Importar los módulos de Python

import pandas as pd
  • Lea los datos del archivo .csv

internet_usage_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/share-of-individuals-using-the-internet.csv"
internet_usage_df = pd.read_csv(internet_usage_url)
  • Añade una animación a la columna year utilizando animation_frame=year

import plotly.express as px

fig = px.choropleth(internet_usage_df, locations="Code",
                    color="Individuals using the Internet (% of population)", # lifeExp is a column of gapminder
                    hover_name="Country", # column to add to hover information
                    animation_frame="Year", # column on which to animate
                    color_continuous_scale=px.colors.sequential.Plasma)
 
fig.update_layout(
    # add a title text for the plot
    title_text = 'Internet usage across the world (% population)',
    # set projection style for the plot
    geo = dict(projection={'type':'natural earth'}) # by default, projection type is set to 'equirectangular'
)
fig.show()
  • Observe que el primer argumento de nuestra función de coropleta es el DataFrame internet_usage_df DataFrame, que contiene registros para todos los años entre 1970- 2017, y no internet_usage_2016, que habíamos utilizado hasta ahora. Si Si utilizáramos el DataFrame internet_usage_2016, obtendríamos un gráfico estático sin deslizante, ya que no habría nada que animar con registros de un solo año. La funcionalidad de la animación y el deslizador es una forma sencilla de ver cómo ha crecido el uso de Internet en diferentes países del mundo a lo largo de los años.

  • Sin embargo, ¡hay algo en el deslizador que resulta curioso! Los años en el deslizador no están en el orden correcto, comienza con 1990, luego llega hasta 2015, y luego vuelve a 1970 y así sucesivamente. La forma más fácil de solucionar este problema es ordenar el DataFrame por tiempo (la variable Year).

  • Ordene el conjunto de datos por Year utilizando el siguiente código

internet_usage_df.sort_values(by=["Year"],inplace=True)
internet_usage_df.head()
Country Code Year Individuals using the Internet (% of population)
5347 Syrian Arab Republic NaN 1960 0.0
718 Burundi BDI 1960 0.0
5493 Togo TGO 1960 0.0
572 Botswana BWA 1960 0.0
3414 Maldives MDV 1960 0.0
  • Vuelva a generar el gráfico animado ahora que la clasificación está hecha

import plotly.express as px

fig = px.choropleth(internet_usage_df, locations="Code",
                    color="Individuals using the Internet (% of population)", # lifeExp is a column of gapminder
                    hover_name="Country", # column to add to hover information
                    animation_frame="Year", # column on which to animate
                    color_continuous_scale=px.colors.sequential.Plasma)
 
fig.update_layout(
    # add a title text for the plot
    title_text = 'Internet usage across the world (% population)',
    # set projection style for the plot
    geo = dict(projection={'type':'natural earth'}) # by default, projection type is set to 'equirectangular'
)
fig.show()
  • Podemos ver que hubo definitivamente un aumento en el uso de Internet entre 1992 y 2010. Hay un punto más que debe ser abordado antes de cerrar nuestra discusión sobre mapas coropléticos mundiales. En su trabajo, es posible que se encuentre con conjuntos de datos que serían interesantes de visualizar en un mapa geográfico pero que no tienen una columna que indique su código ISO 3166-1. En estos casos, puede descargar los códigos de los países desde el sitio web oficial de la ISO: https://www.iban.com/country-codes.

  • Podrá ver el conjunto de datos de códigos de países utilizando el siguiente código

import pandas as pd
country_codes_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/country_codes.tsv"
country_codes = pd.read_csv(country_codes_url, sep='\t')
country_codes.head()
Country Alpha-2 code Alpha-3 code Numeric
0 Afghanistan AF AFG 4
1 Albania AL ALB 8
2 Algeria DZ DZA 12
3 American Samoa AS ASM 16
4 Andorra AD AND 20

Mapas de los estados de EE.UU

  • Aunque el objetivo de muchas visualizaciones es comparar y contrastar características específicas entre países, a menudo también hay contextos en los que necesitamos analizar características entre regiones más pequeñas, como los estados dentro de un país. Para generar mapas coropléticos para los estados de EE.UU. utilizaremos los datos de población por estados que están disponibles en el sitio web del censo de EE.UU. https://www.census.gov/newsroom/press-kits/2018/pop-estimates-national-state.html

Ejercicio 48: Creación de un mapa coroplético de Estados Unidos

  • En este ejercicio, utilizaremos el conjunto de datos de población de los Estados Unidos. Vamos a modificar el conjunto de datos y lo utilizaremos para trazar un mapa de coroplético de todo el estado. A continuación, cambiaremos el diseño de este mapa para mostrar la población de EE.UU. a través de los estados

  • Importar el módulo de Python

import pandas as pd
  • Leer el conjunto de datos de la URL

us_population_url = 'https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/us_state_population.tsv'
df = pd.read_csv(us_population_url, sep='\t')
df.head()
State Code 2010 2011 2012 2013 2014 2015 2016 2017 2018
0 Alabama AL 4785448 4798834 4815564 4830460 4842481 4853160 4864745 4875120 4887871
1 Alaska AK 713906 722038 730399 737045 736307 737547 741504 739786 737438
2 Arizona AZ 6407774 6473497 6556629 6634999 6733840 6833596 6945452 7048876 7171646
3 Arkansas AR 2921978 2940407 2952109 2959549 2967726 2978407 2990410 3002997 3013825
4 California CA 37320903 37641823 37960782 38280824 38625139 38953142 39209127 39399349 39557045
  • Es bueno que este conjunto de datos también tenga los códigos estatales disponibles en la función Código. Sin embargo, los datos no están en el formato que querríamos: están en el formato ancho y necesitamos que sea largo

  • Utilice la función melt para convertir los datos al formato deseado

df = pd.melt(df, id_vars=['State', 'Code'], var_name="Year", value_name="Population")
df.head()
State Code Year Population
0 Alabama AL 2010 4785448
1 Alaska AK 2010 713906
2 Arizona AZ 2010 6407774
3 Arkansas AR 2010 2921978
4 California CA 2010 37320903
  • Una vez que sepa cómo generar un mapa coroplético de los países del mundo, un mapa coroplético de los estados de EE.UU. es bastante sencillo. A diferencia del caso de generar un mapa coroplético mundial en el que utilizamos el módulo plotly express utilizaremos el módulo graph_objects para generar el mapa coroplético para los estados en los Estados Unidos

  • Importar el módulo graph_objects

import plotly.graph_objects as go
  • Inicialice la figura con la función Figure en graph_objects. Específicamente, el argumento de datos debe ser una instancia de la clase Choropleth con los siguientes parámetros

    • locations: Se establece en la columna del DataFrame que contiene los códigos de los nombres de los estados. los códigos de los nombres

    • z: Se establece en la columna que contiene la característica numérica con la que el mapa debe ser codificado por colores

    • locationmode: Este se establece como Estados Unidos.

    • colorscale: Esto se establece en un esquema de colores, como Blues | Reds | Greens. Para más opciones, consulte la documentación oficial de plotly: https://plot.ly/python/referencia/.

    • colorbar_title: Esto se establece en el título de la barra de color de la derecha, indicando la correspondencia de los valores de color y característica.

# initialize the figure
fig = go.Figure(
    data=go.Choropleth(
        locations=df['Code'], # Code for US states
        z = df['Population'].astype(int), # Data to be color-coded
        locationmode = 'USA-states', # set of locations match entries in 'locations'
        colorscale = 'Blues',
        colorbar_title = "Population"
    )
)
  • Realice cambios en el diseño con update_layout() establezca title_text y geo_scope

# update layout
fig.update_layout(
    title_text = 'US Population across states',
    geo_scope='usa', # limit map scope to USA
)
fig.show()
  • Los mapas coropléticos son una forma eficaz de visualizar estadísticas agregadas en las divisiones de una región geográfica. Se pueden utilizar dos módulos de plotly express y graph_objects para generar mapas coropléticos interactivos. Los módulos mapean registros de de divisiones, como países y estados, a ubicaciones en mapas geográficos utilizando un sistema de nombres de códigos estandarizados de países y estados

Gráficos sobre mapas geográficos

  • Mientras que los gráficos anteriores eran excelentes para visualizar tendencias más globales como países o estados, ¿qué pasa si queremos representar características en regiones más pequeñas, digamos dentro de los estados individuales? En esta sección, aprenderá a dibujar gráficos de dispersión y gráficos de burbujas en los mapas.

Ejercicio 49: Creación de un gráfico de dispersión en un mapa geográfico

  • Vamos a trazar las ubicaciones de las tiendas Walmart en un mapa de los Estados Unidos. Este conjunto de datos está disponible públicamente en: https://github.com/plotly/datasets/ sitio web de plotly. En este ejercicio, utilizaremos el conjunto de datos de aperturas de tiendas Walmart de 1962 a 2006.

  • Para crear un gráfico de dispersión a partir de este conjunto de datos, utilizaremos el módulo graph_objects. Encontraremos una ubicación de interés en el mapa y asignaremos longitudes y latitudes en ese mapa y averiguaremos el número de aperturas de tiendas Walmart para diferentes partes de los Estados Unidos.

  • Importar módulos de Python

import pandas as pd
  • Lee los datos de la URL

walmart_locations_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/1962_2006_walmart_store_openings.csv"
walmart_loc_df = pd.read_csv(walmart_locations_url)
walmart_loc_df.head()
storenum OPENDATE date_super conversion st county STREETADDR STRCITY STRSTATE ZIPCODE type_store LAT LON MONTH DAY YEAR
0 1 7/1/62 3/1/97 1.0 5 7 2110 WEST WALNUT Rogers AR 72756 Supercenter 36.342235 -94.07141 7 1 1962
1 2 8/1/64 3/1/96 1.0 5 9 1417 HWY 62/65 N Harrison AR 72601 Supercenter 36.236984 -93.09345 8 1 1964
2 4 8/1/65 3/1/02 1.0 5 7 2901 HWY 412 EAST Siloam Springs AR 72761 Supercenter 36.179905 -94.50208 8 1 1965
3 8 10/1/67 3/1/93 1.0 5 29 1621 NORTH BUSINESS 9 Morrilton AR 72110 Supercenter 35.156491 -92.75858 10 1 1967
4 7 10/1/67 NaN NaN 5 119 3801 CAMP ROBINSON RD. North Little Rock AR 72118 Wal-Mart 34.813269 -92.30229 10 1 1967
  • Volveremos a utilizar el módulo graph_objects para generar nuestro gráfico de dispersión en el mapa de Estados Unidos. En cuanto al mapa de coropletas, utilizaremos la función Figure de graph_objects y la función update_layout(). Sin embargo, esta vez asignaremos una instancia de la clase Scattergeo como argumento de Figure(). Pasaremos las longitudes y latitudes de nuestras localizaciones de interés utilizando los parámetros lon y lat

  • Trazar el gráfico de dispersión utilizando la función update_layout

import plotly.graph_objects as go

fig = go.Figure(data=go.Scattergeo(
    lon = walmart_loc_df['LON'], # column containing longitude information of the locations to plot
    lat = walmart_loc_df['LAT'], # column containing latitude information of the locations to plot
    text = walmart_loc_df['STREETADDR'], # column containing value to be displayed on hovering over the map
    mode = 'markers' # a marker for each location
))

fig.update_layout(
    title = 'Walmart stores across world',
    geo_scope='usa',
)
fig.show()
  • Una observación sorprendente es que Walmart es mucho más prominente en el este de los EE.UU. que en el oeste

Ejercicio 50: Creación de un gráfico de burbujas en un mapa geográfico

  • Dado que la parte oriental del mapa de EE.UU. parece estar muy densamente poblada de tiendas Walmart, podría ser una buena idea mostrar una característica agregada, como el recuento de tiendas Walmart en los diferentes estados. Los gráficos de burbujas están diseñados exactamente para este tipo de visualización. En el contexto actual de la visualización de datos geográficos, los gráficos de burbujas son gráficos con tantas burbujas como regiones de interés, en los que el tamaño de las burbujas depende del valor que indiquen, cuanto mayor sea el valor, mayor será la burbuja

  • En este ejercicio, utilizaremos el conjunto de datos de aperturas de tiendas Walmart de 1962 a 2006 y generar un gráfico de burbujas para ver el número de tiendas Walmart en los diferentes estados de los Estados Unidos. A continuación, veremos otro contexto y generaremos un gráfico de burbujas utilizando el conjunto de datos Internet_usage para averiguar el número de usuarios de Internet en todo el mundo. También animaremos el gráfico de burbujas para mostrar el aumento del número de usuarios de Internet en todo el mundo

  • Importar módulos de Python

import pandas as pd
  • Lee los datos de la URL

walmart_locations_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/1962_2006_walmart_store_openings.csv"
walmart_loc_df = pd.read_csv(walmart_locations_url)
walmart_loc_df.head()
storenum OPENDATE date_super conversion st county STREETADDR STRCITY STRSTATE ZIPCODE type_store LAT LON MONTH DAY YEAR
0 1 7/1/62 3/1/97 1.0 5 7 2110 WEST WALNUT Rogers AR 72756 Supercenter 36.342235 -94.07141 7 1 1962
1 2 8/1/64 3/1/96 1.0 5 9 1417 HWY 62/65 N Harrison AR 72601 Supercenter 36.236984 -93.09345 8 1 1964
2 4 8/1/65 3/1/02 1.0 5 7 2901 HWY 412 EAST Siloam Springs AR 72761 Supercenter 36.179905 -94.50208 8 1 1965
3 8 10/1/67 3/1/93 1.0 5 29 1621 NORTH BUSINESS 9 Morrilton AR 72110 Supercenter 35.156491 -92.75858 10 1 1967
4 7 10/1/67 NaN NaN 5 119 3801 CAMP ROBINSON RD. North Little Rock AR 72118 Wal-Mart 34.813269 -92.30229 10 1 1967
  • Utilice la función groupby para calcular el número de tiendas Walmart por estado

walmart_stores_by_state = walmart_loc_df.groupby('STRSTATE').count()['storenum'].reset_index().rename(columns={'storenum':'NUM_STORES'})
walmart_stores_by_state.head()
STRSTATE NUM_STORES
0 AL 90
1 AR 81
2 AZ 55
3 CA 159
4 CO 56
  • Para generar los gráficos de burbujas, utilizaremos el módulo plotly express y la función scatter_geo. Obsérvese que el parámetro locations se establece con el nombre de la columna que contiene los códigos de estado, y el parámetro size se ajusta a la función función NUM_STORES

import plotly.express as px
fig = px.scatter_geo(walmart_stores_by_state,
                     locations="STRSTATE", # name of column which contains state codes
                     size="NUM_STORES", # name of column which contains aggregate value to visualize
                     locationmode = 'USA-states',
                     hover_name="STRSTATE",
                     size_max=45)

fig.update_layout(
    # add a title text for the plot
    title_text = 'Walmart stores across states in the US',
    # limit plot scope to USA
    geo_scope='usa'
)
fig.show()
  • ¿Se te ocurre algún otro contexto en el que un gráfico de burbujas pueda ser útil para visualización? ¿Qué tal si revisamos los datos de uso de Internet (sobre los porcentajes de la población que utiliza Internet en cada país) para generar un gráfico de burbujas a nivel mundial?

  • Resulta que el recuento de individuos que utilizan Internet en cada país también está disponible en el mismo recurso (Our World in Data: https://ourworldindata. org/internet) que hemos utilizado para recoger nuestros datos anteriores.

  • Utilice el siguiente código para leer los datos de los usuarios de Internet por país conjunto de datos

import pandas as pd

internet_users_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/number-of-internet-users-by-country.csv"
internet_users_df = pd.read_csv(internet_users_url)
internet_users_df.head()
Country Code Year Number of internet users (users)
0 Afghanistan AFG 1990 0
1 Afghanistan AFG 2001 990
2 Afghanistan AFG 2002 1003
3 Afghanistan AFG 2003 20272
4 Afghanistan AFG 2004 25520
  • Ordenar el DataFrame por la característica Year

internet_users_df.sort_values(by=['Year'],inplace=True)
internet_users_df.head()
Country Code Year Number of internet users (users)
0 Afghanistan AFG 1990 0
1257 Eritrea ERI 1990 0
1236 Equatorial Guinea GNQ 1990 0
4016 Timor TLS 1990 0
1214 El Salvador SLV 1990 0
  • Traza el número de usuarios que utilizan Internet en todo el mundo en 2016

import plotly.express as px

fig = px.scatter_geo(internet_users_df.query("Year==2016"),
                     locations="Code", # name of column indicating country-codes
                     size="Number of internet users (users)", # name of column by which to size the bubble
                     hover_name="Country", # name of column to be displayed while hovering over the map
                     size_max=80, # parameter to scale all bubble sizes
                     color_continuous_scale=px.colors.sequential.Plasma)
 
fig.update_layout(
    # add a title text for the plot
    title_text = 'Internet users across the world - 2016', # set projection style for the plot
    geo = dict(projection={'type':'natural earth'}) # by default, projection type is set to 'equirectangular'
)
fig.show()
  • ¿Notas que el mayor número de usuarios procede de India y China? Como sabemos por nuestro conjunto de datos anterior que el porcentaje de la población que utiliza Internet en estos países es bajo, este gran grupo de usuarios puede atribuirse a la población de estos países.

  • Anima el gráfico de burbujas para mostrar el aumento del número de usuarios de Internet a lo largo de los años utilizando el parámetro animation_frame:

import plotly.express as px

fig = px.scatter_geo(internet_users_df,
                     locations="Code", # name of column indicating country-codes
                     size="Number of internet users (users)", # name of column by which to size the bubble
                     hover_name="Country", # name of column to be displayed while hovering over the map
                     size_max=80, # parameter to scale all bubble size
                     animation_frame="Year"
                    )
 
fig.update_layout(
    # add a title text for the plot
    title_text = 'Internet users across the world', # set projection style for the plot
    geo = dict(projection={'type':'natural earth'}) # by default, projection type is set to 'equirectangular'
)
fig.show()
  • Podemos ver en los dos gráficos anteriores cómo el número de usuarios de Internet aumentó entre 2001 y 2016 en Estados Unidos. Los gráficos de dispersión en los mapas pueden utilizarse para mostrar ubicaciones específicas de interés en los mapas geográficos, mientras que los mapas de burbujas son una buena forma de presentar los datos de recuento a través de diferentes fuentes.

Líneas sobre mapas geográficos

  • Los gráficos de líneas representados en mapas son otra clase importante de visualización de datos geográficos. Para esta sección, utilizaremos los datos de aeropuertos y vuelos del conjunto de datos de retrasos y cancelaciones de vuelos de 2015 publicado por el U.S. Department of Transportation’s (DOT). Bureau of Transportation Statistics

  • Como el conjunto de datos es enorme, solo incluiremos los datos de todos los vuelos con retrasos de las aerolíneas el 1 de enero de 2015.

  • airports.csv: Contiene atributos de ubicación como la información de latitud y longitud para todos los aeropuertos

  • new_year_day_2015_delayed_flights.csv: Contiene detalles de los vuelos, como los números de vuelo, aeropuertos de origen y destino de todos los vuelos del subconjunto seleccionado.

51: Creación de gráficos de líneas en un mapa geográfico

  • En este ejercicio, utilizaremos el conjunto de datos de los aeropuertos y generaremos primero un gráfico de dispersión para averiguar la ubicación de todos los aeropuertos de Estados Unidos. A continuación, combinaremos los dos DataFrames (flights y airport_record) para obtener la longitud y latitud de los de los aeropuertos de origen de todos los vuelos y dibujaremos líneas desde el aeropuerto de origen hasta el aeropuerto de destino de cada vuelo utilizando esta fusión.

  • Cargue primero el conjunto de datos de los aeropuertos

import pandas as pd
us_airports_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/airports.csv"
us_airports_df = pd.read_csv(us_airports_url)
us_airports_df.head()
IATA_CODE AIRPORT CITY STATE COUNTRY LATITUDE LONGITUDE
0 ABE Lehigh Valley International Airport Allentown PA USA 40.65236 -75.44040
1 ABI Abilene Regional Airport Abilene TX USA 32.41132 -99.68190
2 ABQ Albuquerque International Sunport Albuquerque NM USA 35.04022 -106.60919
3 ABR Aberdeen Regional Airport Aberdeen SD USA 45.44906 -98.42183
4 ABY Southwest Georgia Regional Airport Albany GA USA 31.53552 -84.19447
  • Generar un gráfico de dispersión en el mapa de Estados Unidos para indicar la ubicación de todos los aeropuertos en nuestro conjunto de datos, utilizando el módulo graph_objects

import plotly.graph_objects as go

fig = go.Figure()
fig.add_trace(go.Scattergeo(locationmode = 'USA-states',
                            lon = us_airports_df['LONGITUDE'],
                            lat = us_airports_df['LATITUDE'],
                            hoverinfo = 'text',
                            text = us_airports_df['AIRPORT'],
                            mode = 'markers',
                            marker = dict(size = 5,color = 'black')))
fig.update_layout(
    title_text = 'Airports in the USA',
    showlegend = False,
    geo = go.layout.Geo(scope = 'usa')
)
fig.show()
  • Al pasar el ratón por encima de un punto de datos, aparecerá el nombre del aeropuerto estadounidense. ¿Se ha dado cuenta de que hay una función add_trace() además de la habitual creación de instancias de la clase Scattergeo? La función add_trace se utiliza porque vamos a superponer nuestros datos de vuelo en forma de líneas sobre este gráfico de dispersión en el mapa. La función add_trace permite a plotly tratar el gráfico de dispersión y los trazados de líneas como múltiples capas en el mapa.

  • Cargar el archivo que contiene los registros de vuelo

new_year_2015_flights_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/new_year_day_2015_delayed_flights.csv"
new_year_2015_flights_df = pd.read_csv(new_year_2015_flights_url)
new_year_2015_flights_df.head()
YEAR MONTH DAY DAY_OF_WEEK AIRLINE FLIGHT_NUMBER TAIL_NUMBER ORIGIN_AIRPORT DESTINATION_AIRPORT SCHEDULED_DEPARTURE ... ARRIVAL_TIME ARRIVAL_DELAY DIVERTED CANCELLED CANCELLATION_REASON AIR_SYSTEM_DELAY SECURITY_DELAY AIRLINE_DELAY LATE_AIRCRAFT_DELAY WEATHER_DELAY
0 2015 1 1 4 HA 17 N389HA LAS HNL 145 ... 610.0 15.0 0 0 NaN 0.0 0.0 15.0 0.0 0.0
1 2015 1 1 4 B6 2134 N307JB SJU MCO 400 ... 730.0 85.0 0 0 NaN 0.0 0.0 85.0 0.0 0.0
2 2015 1 1 4 B6 2276 N646JB SJU BDL 438 ... 908.0 89.0 0 0 NaN 17.0 0.0 72.0 0.0 0.0
3 2015 1 1 4 US 425 N174US PDX PHX 520 ... 950.0 60.0 0 0 NaN 0.0 0.0 60.0 0.0 0.0
4 2015 1 1 4 AA 89 N3KVAA IAH MIA 520 ... 935.0 54.0 0 0 NaN 0.0 0.0 54.0 0.0 0.0

5 rows × 31 columns

  • Junto con los aeropuertos de origen y destino de cada vuelo, necesitamos tener la información de longitud y latitud de los aeropuertos correspondientes. Para ello necesitamos fusionar los DataFrames que contienen los datos del aeropuerto y del vuelo. Primero fusionamos los dos conjuntos de datos para obtener las longitudes y latitudes de los de todos los vuelos

# merge the DataFrames on origin airport codes
new_year_2015_flights_df = new_year_2015_flights_df.merge(us_airports_df[['IATA_CODE','LATITUDE','LONGITUDE']], \
                                                          left_on='ORIGIN_AIRPORT', \
                                                          right_on='IATA_CODE', \
                                                          how='inner')

# drop the duplicate column containing airport code
new_year_2015_flights_df.drop(columns=['IATA_CODE'],inplace=True)

# rename the latitude and longitude columns to reflect that they correspond to the origin airport
new_year_2015_flights_df.rename(columns={"LATITUDE":"ORIGIN_AIRPORT_LATITUDE", "LONGITUDE":"ORIGIN_AIRPORT_LONGITUDE"},inplace=True)
new_year_2015_flights_df.head()
YEAR MONTH DAY DAY_OF_WEEK AIRLINE FLIGHT_NUMBER TAIL_NUMBER ORIGIN_AIRPORT DESTINATION_AIRPORT SCHEDULED_DEPARTURE ... DIVERTED CANCELLED CANCELLATION_REASON AIR_SYSTEM_DELAY SECURITY_DELAY AIRLINE_DELAY LATE_AIRCRAFT_DELAY WEATHER_DELAY ORIGIN_AIRPORT_LATITUDE ORIGIN_AIRPORT_LONGITUDE
0 2015 1 1 4 HA 17 N389HA LAS HNL 145 ... 0 0 NaN 0.0 0.0 15.0 0.0 0.0 36.08036 -115.15233
1 2015 1 1 4 HA 7 N395HA LAS HNL 900 ... 0 0 NaN 0.0 0.0 23.0 0.0 0.0 36.08036 -115.15233
2 2015 1 1 4 AA 1623 N438AA LAS DFW 905 ... 0 0 NaN 4.0 0.0 133.0 0.0 0.0 36.08036 -115.15233
3 2015 1 1 4 DL 1530 N954DN LAS MSP 920 ... 0 0 NaN 1.0 0.0 15.0 0.0 0.0 36.08036 -115.15233
4 2015 1 1 4 WN 1170 N902WN LAS ELP 950 ... 0 0 NaN 5.0 0.0 11.0 0.0 0.0 36.08036 -115.15233

5 rows × 33 columns

  • Ahora, realizaremos una fusión similar para obtener los datos de latitud y longitud de los aeropuertos de destino de todos los vuelos

# merge the DataFrames on destination airport codes
new_year_2015_flights_df = new_year_2015_flights_df.merge(us_airports_df[['IATA_CODE','LATITUDE','LONGITUDE']], \
                                                          left_on='DESTINATION_AIRPORT', \
                                                          right_on='IATA_CODE', \
                                                          how='inner')

# drop the duplicate column containing airport code
new_year_2015_flights_df.drop(columns=['IATA_CODE'],inplace=True)

# rename the latitude and longitude columns to reflect that they correspond to the destination airport
new_year_2015_flights_df.rename(columns={'LATITUDE':'DESTINATION_AIRPORT_LATITUDE', 'LONGITUDE':'DESTINATION_AIRPORT_LONGITUDE'},inplace=True)
new_year_2015_flights_df.head()
YEAR MONTH DAY DAY_OF_WEEK AIRLINE FLIGHT_NUMBER TAIL_NUMBER ORIGIN_AIRPORT DESTINATION_AIRPORT SCHEDULED_DEPARTURE ... CANCELLATION_REASON AIR_SYSTEM_DELAY SECURITY_DELAY AIRLINE_DELAY LATE_AIRCRAFT_DELAY WEATHER_DELAY ORIGIN_AIRPORT_LATITUDE ORIGIN_AIRPORT_LONGITUDE DESTINATION_AIRPORT_LATITUDE DESTINATION_AIRPORT_LONGITUDE
0 2015 1 1 4 HA 17 N389HA LAS HNL 145 ... NaN 0.0 0.0 15.0 0.0 0.0 36.08036 -115.15233 21.31869 -157.92241
1 2015 1 1 4 HA 7 N395HA LAS HNL 900 ... NaN 0.0 0.0 23.0 0.0 0.0 36.08036 -115.15233 21.31869 -157.92241
2 2015 1 1 4 UA 253 N768UA IAH HNL 920 ... NaN 8.0 0.0 42.0 0.0 0.0 29.98047 -95.33972 21.31869 -157.92241
3 2015 1 1 4 UA 328 N210UA DEN HNL 1130 ... NaN 0.0 0.0 31.0 0.0 0.0 39.85841 -104.66700 21.31869 -157.92241
4 2015 1 1 4 UA 1173 N56859 SFO HNL 805 ... NaN 0.0 0.0 17.0 0.0 0.0 37.61900 -122.37484 21.31869 -157.92241

5 rows × 35 columns

  • Ahora, dibujaremos nuestros gráficos de líneas. Para cada vuelo, tenemos que dibujar una línea entre el aeropuerto de origen y el de destino. Esto se hace proporcionando los valores de latitud y longitud de los aeropuertos de destino y origen a los parámetros lon y lat de Scattergeo y ajustando el modo a líneas en lugar de marcadores. Además, fíjate en que aquí estamos utilizando otra función add_trace

#Now, we will draw our line plots -- for each flight, we need to draw a line between the origin and destination airport. This is done by providing the latitude and longitude values of destination and origin airports to the lonand lat parameters of Scattergeo and setting mode to 'lines' instead of 'markers'. Also, notice that we are using another add_trace function here. It may take a few minutes for the plot to show the flight routes.
for i in range(len(new_year_2015_flights_df)):
    fig.add_trace(
        go.Scattergeo(
            locationmode = 'USA-states',
            lon = [new_year_2015_flights_df['ORIGIN_AIRPORT_LONGITUDE'][i], new_year_2015_flights_df['DESTINATION_AIRPORT_LONGITUDE'][i]],
            lat = [new_year_2015_flights_df['ORIGIN_AIRPORT_LATITUDE'][i], new_year_2015_flights_df['DESTINATION_AIRPORT_LATITUDE'][i]],
            mode = 'lines',
            line = dict(width = 1,color = 'red')
        )
    )
    
fig.update_layout(
    title_text = 'Delayed flight on Jan 1, 2015 in USA',
    showlegend = False,
    geo = go.layout.Geo(
        scope = 'usa'
    ),
)
  
fig.show()
  • Los trazados de líneas en los mapas geográficos pueden ser generados usando el módulo graph_objects de plotly.

Tarea 2.1

  • Pasos principales

    1. Cargar el conjunto de datos de producción de energía renovable.

    2. Ordenar el DataFrame de producción en función de la característica Year.

    3. Generar un mapa de coropletas para la producción de energía renovable utilizando el módulo plotly express animado en función del Year.

    4. Actualice el diseño para incluir un estilo de proyección adecuado y un texto de título, y luego muestre la figura.

    5. Cargue el conjunto de datos de consumo de energía renovable.

    6. Convierta el DataFrame de consumo a un formato adecuado para su visualización.

    7. Ordenar el DataFrame de consumo en base a la característica Year.

    8. Generar un mapa coroplético para el consumo de energía renovable utilizando el módulo plotly express animado en función del año.

    9. Actualice el diseño para incluir un estilo de proyección adecuado y un texto de título, y luego muestre la figura.

  • Resumen

En este capítulo hemos presentado tres tipos diferentes de visualización de datos geográficos, gráficos de dispersión y de burbujas sobre mapas geográficos y gráficos de líneas sobre mapas geográficos. Los mapas coropléticos presentan estadísticas agregadas a través de diferentes regiones de los mapas geográficos. Los gráficos de dispersión son eficaces para indicar detalles detalles sobre lugares específicos de interés, mientras que los gráficos de burbujas son útiles para presentar datos de recuento por región en un mapa. Los gráficos de líneas son útiles para visualizar las rutas de de los sistemas de transporte, por ejemplo. Estos gráficos pueden generarse fácilmente con los módulos plotly express y graph_objects. La animación puede realizarse con respecto a una característica numérica discreta en un conjunto de datos.

Modelo predictivo con series de tiempo

  • Definición: Un proceso estocástico es una colección o familia de variables aleatorias \(\{X_{t}\}_{t\in I}\) ordenadas según el subíndice \(t\) que en general se suele identificar con el tiempo. Llamamos trayectoria del proceso a una realización del proceso estocástico. Si \(I\) es discreto, el proceso es en tiempo discreto. Si \(I\) es continuo, el proceso es en tiempo continuo.

  • Observación:

    • Por tanto, para cada instante \(t\) tendremos una variable aleatoria distinta representada por \(X_{t}\), con lo que un proceso estocástico puede interpretarse como una sucesión de variables aleatorias cuyas características pueden variar a lo largo del tiempo.

    • Un ejemplo de proceso en tiempo discreto se obtiene para \(I = \{1,\dots,n\}\). En este caso, el proceso es \(Y_{1}, Y_{2},\dots, Y_{n}\), y una trayectoria se denota por \(y_{1}, y_{2},\dots, y_{n}\). Un ejemplo de proceso en tiempo continuo se obtiene para \(I=[0, T], I=[0,\infty], I=(-\infty,\infty)\)

  • Definición: Una serie temporal es una realización parcial de un proceso estocástico de parámetro tiempo discreto, donde los elementos de \(I\) están ordenados y corresponden a instantes equidistantes del tiempo.

  • Definición: Un proceso estocástico \((Y_{t})\) es estacionario cuando las propiedades estadísticas de cualquier secuencia finita \(Y_{t_{1}}, Y{t_{2}},\dots, Y{t_{n}}, (n\geq1)\) de componentes de \((Y_{t})\) son semejantes a las de la secuencia \(Y_{t_{1}+h}, Y_{t_{2}+h},\dots, Y_{t_{n}+h}\) para cualquier número entero \(h = \pm1, \pm2,\dots\)

  • Definición: Un proceso estocástico \((Y_{t})\) es no estacionario cuando las propiedades estadísticas de al menos una secuencia finita \(Y_{t_{1}}, Y{t_{2}},\dots, Y{t_{n}}, (n\geq1)\) de componentes de \((Y_{t})\), son diferentes de las de la secuencia \(Y_{t_{1}+h}, Y_{t_{2}+h},\dots, Y_{t_{n}+h}\) para al menos un número entero \(h>0\).

  • Definición: La covarianza entre \(y_{t}\) y su valor en otro periodo de tiempo, digamos, \(y_{t+k}\) se denomina autocovarianza en el retardo \(k\), y se define como $\(\gamma_{k}=Cov(y_{t}, y_{t+k})=E[(y_{t}-\mu)(y_{t+k}-\mu)]\)\( La colección de los valores de \)\gamma_{k}, k = 0, 1, 2,\dots\( se denomina función de autocovarianza. Obsérvese que la autocovarianza en el lag \)k = 0\( es simplemente la varianza de la serie temporal; es decir, \)\gamma_{0} = \sigma_{y}^{2}\(, que es constante para una serie temporal estacionaria. El coeficiente de autocorrelación en el lag \)k\( para una serie temporal estacionaria es \)\(\rho_{k}=\frac{E[(y_{t}-\mu)(y_{t+k}-\mu)]}{\sqrt{E[(y_{t}-\mu)^{2}]E[(y_{t+k}-\mu)^{2}]}}=\frac{Cov(y_{t}, y_{t+k})}{Var(y_{t})}=\frac{\gamma_{k}}{\gamma_{0}}\)\( La colección de los valores de \)\rho_{k}, k = 0, 1, 2,\dots$ se llama función de autocorrelación (ACF).

  • Definición: Un modelo para un proceso estocástico es cualquier conjunto de hipótesis bien definidas sobre las propiedades estadísticas de dicho proceso.

Introducción al modelo ARIMA

Los modelos ARIMA (Autorregresivo integrado de media móvil) aproximan los valores futuros de una serie temporal como una función lineal de observaciones pasadas y términos de ruido blanco. Una serie de tiempo \(y_{t}\) se llama un proceso de media móvil integrada autorregresiva (ARIMA) de órdenes \(p, d\), y \(q\), denotado ARIMA\((p, d, q)\) si su diferencia \(d\) da lugar a un proceso estacionario ARMA\((p, q)\). Por lo tanto, un ARIMA\((p, d, q)\) puede escribirse como

\[\Phi(B)(1-B)^{d}y_{t}=\delta+\Theta(B)\varepsilon_{t}\]

where $\(\Phi(B)=1-\sum_{i=1}^{p}\phi_{i}B^{i}\quad\text{and}\quad\Theta(B)=1-\sum_{i=1}^{q}\theta_{i}B^{i}\)$

son los términos del operador back-shit en los AR\((p)\) y MA\((q)\) definidos como \(\Phi(B)y_{t}=\delta+\varepsilon_{t}\) y \(y_{t}=\mu+\Theta(B)\varepsilon_{t}\), con \(\delta=\mu-\phi\mu\), donde \(\mu\) es la media y \(\varepsilon_{t}\) el ruido blanco con \(E(\varepsilon_{t})=0\). Así, una vez realizada la diferenciación y una serie temporal estacionaria \(w_{t}=(1-B)^{d}y_{t}\) es obtenida, los métodos autoregresivo de orden \(p\): AR\((p)\) y media movil de orden \(q\): MA\((q)\) pueden ser aplicados para tener un modelo completo.

Implementación del modelo ARIMA

  • Durante esta sección estudiaremos el uso de las APIs para cargar datos de interes de estudio, los cuales en esta sección corresponden a series de tiempo financieras, especificamente la serie de tiempo de las acciones de Apple cotizadas en bolsas de valores será considerada.

  • En las secciones pasadas los datos eran cargados a un DataFrame usando la función read_csv de Python utlizando el link de github en formato raw o simplemente con la dirección del directorio local donde reposaba el archivo en formato CSV que deseabamos cargar, en éste caso utlizaremos un protocolo de internet intermediario que nos permitirá obtener dichos datos.

  • Las series de tiempo financieras son de gran interés personal, pero estos modelos pueden ser aplicados a cualquier tipo de proceso discreto que requiera de predicciones para la correcta toma de decisiones.

  • ¿Qué es una API? API es el acrónimo de Application Programming Interface (Interfaz de Programación de Aplicaciones), que es un intermediario de software que permite que dos aplicaciones se comuniquen entre sí. Cada vez que usas una aplicación como Facebook, envías un mensaje instantáneo o consultas el tiempo en tu teléfono, estás usando una API. En su mayoría las API son privadas, algunas como la de yahoo finance son libres hasta cierto límite, y para cierto futuros y derivados especificos. Algunas entregan una versión de prueba, luego cuando la compañia que la ofrece nota un gran número de consultas (queries) por medio de su API a sus servidores desde una IP determinada, o de un Bot por ejemplo que hayas programado, comienzas a obtener mensajes de error direccionados a tu IP revocando la conexión. Otra opción interesante es Quandl, pero desafortunadamente ahora es adquisición de Nasdaq y lo más seguro es que exisitrán ciertos costos para el uso de la API que no existian antes. Una API que esperamos se mantenga gratuita es la de finnhub

  • Finnhub Stock API: En esta sección estaremos utlizando la API de finnhub. Finnhub proporciona una API de acciones en tiempo real gratuita para todos los operadores. También puede acceder a los datos de la empresa, el precio objetivo, las tendencias de recomendación, las ganancias y los datos de compensación de los directores generales a través de la API. Con la única misión de democratizar los datos financieros, finnhub ofrece una API gratuita en tiempo real para acciones, divisas y criptodivisas. Con esta API, podrá acceder a datos en tiempo real de más de 60 bolsas de valores, 10 corredores de divisas y más de 15 criptobolsas. También proporciona noticias en tiempo real para los mercados de empresas, acciones, divisas y criptomonedas. Los datos alternativos y de referencia de las empresas mundiales también se pueden recuperar a través de la API de finnhub.

  • Para hacer uso de la API de finnhub, debes crear una cuenta en su sitio web utilizando el correo de tu preferencia. Al crear tu cuenta automáticamente se generará una API Key que te permitirá ha cer uso de la API. Esta API Key es privada, la puedes volver a generar si la pierdes o si si está siendo usada por otra persona. La siguiente es una imágen del Dashboard de finnhub donde aparece tu API Key. Puedes hacer uso de Sandbox API Key para reducir el coste y el riesgo asociados a las llamadas a API de terceros durante los tests y permitir el desarrollo simultáneos para acelerar los ciclos de desarrollo de aplicaciones y reducir el time-to-market. Se utiliza para probar programas sospechosos que pueden contener virus u otro malware, sin permitir que el software dañe los dispositivos anfitriones. También puede usar el Webhook de finnhub, como método de alteración del funcionamiento de una página o aplicación web, con callbacks personalizados. Es esta sección utlizaremos sólo la API Key, el resto de funcionalidades las podrá experimentar en los informes o proyectos finales. Para mas información acerca de la API de finnhub visitar la documentación correspondiente en su sitio web: https://finnhub.io/docs/api

  • Para usar la API de finnhub desde su notebook, deberá primero instalar usando el gestor de instalación pip la librería de Python requerida para hacer uso de la API usando la orden: pip install finnhub-python. Luego de hacer esto puede comenzar a usar las diferentes funciones públicas asociadas a la clase finnhub que importamos desde Python luego de la isntalación. También importaremos librería para conversiones de unix time a datetime que realizará conversiones a un formato de fecha conocido, a saber: año, mes, día %Y-%m-%d. Las librerías seaborn y matplotlib ya usadas en anteriores secciones también son importadas para realizar los gráficos correspondientes

import finnhub
import pandas as pd
from time import mktime
from datetime import datetime, timedelta
import time
import seaborn as sns
import matplotlib.pyplot as plt
  • La librería warnings es utlizada para evitar múltiples mensaje molestos de alertas no fatales, que en gran parte de los casos están relacionados con diferentes versiones de la librería en uso.

import warnings
warnings.filterwarnings("ignore")
  • Usamos las funciones set_theme() para configurar el tema a usar en las figuras, en éste caso paper el cual invocamos usando set_context("paper"). Para ver más temas para figuras con seaborn visitar: http://seaborn.pydata.org/tutorial/aesthetics.html

sns.set_theme()
sns.set_context("paper")
  • En la siguiente línea hacemos usa de la función Client de la clase finnhub que se encarga de realizar la conexión con el cliente finnhub, utlizando nuesta API Key y crea un objeto instanciado que podemos usar para hacer uso de toda la funcionalidad ofrecida por la API. Debe escribir la API correctamente y entre comillas para que se realice correctamente la conexión. Dependiendo de su conexión a internet y su computadora, puede obtener mensajes de delay por parte del cliente, en estos caso simplemente vuelva a ejecutar la conexión con su API

finnhub_client = finnhub.Client(api_key="c5g56siad3id0d5no4vg")
  • Seleccione el stock de interés. Para éste ejemplo consideramos el precio de las acciones de AAPL con un timeframe diario. La función datetime.now() nos entrega la fecha y hora actual, la función timedelta() nos entrega en tiempo cero, 365 días del año que vamos a restar de la fecha actual. Esto es nuestra serie de tiempo corresponde a los últimos 365 precios de las acciones de Apple

stock = 'AAPL'
resolution = 'D'

end_date = datetime.now()
start_date = end_date - timedelta(days=365)
  • Para obtener información de nuestra vela de precios (candlestick) procedemos a hacer uso de la función stock_candles de la clase finnhub_client para poder obtener información en la vela. Normalemente esta información corresponde al precio al que la acción en en día indicado, abrió, cerró, su valor máximo y mínimo, volumen tradeado y el tiempo dado en días en éste caso. Para aplicaciones avanzadas como las de HFT, que necesitan los precios a una frecuencia más alta cambiaríamos la resolución resolution = 'D' a la resolución que necesite, puede realizar hasta 60 llamados por minuto. La función .timestamp nos permitirá obtener nuestra fecha en formato unix time, único formato que aceptará la función stock_candles. Imprima alguno de estos códigos unix time y utilice la siguiente calculadora https://www.epochconverter.com/ para verificar por ejemplo el código de la fecha y hora actual. Observe que la columna t del DataFrame viene en formato unix time. Además nótese que se realizó un cambio de formato a DataFrame, esto es porque el formato original que viene de finnhub es tipo JSON. Para facilitar el desarrollo de esta sección transformamos éste objeto JSON a un pandas DataFrame. Cada key del archivo JSON corresponde a los combres de cada columna y el value, es una lista con los 365 datos correspondientes a la columna

end = int(end_date.timestamp())
start = int(start_date.timestamp())

AAPL_res = finnhub_client.stock_candles(stock, resolution, start, end)
AAPL_df = pd.DataFrame(AAPL_res)

AAPL_df.head()
c h l o s t v
0 116.32 121.9900 116.05 120.50 ok 1604880000 154515315
1 115.97 117.5900 114.13 115.55 ok 1604966400 138023390
2 119.49 119.6300 116.44 117.19 ok 1605052800 112294954
3 119.21 120.5300 118.57 119.62 ok 1605139200 103350674
4 119.26 119.6717 117.87 119.44 ok 1605225600 81688586
  • La siguiente función la utlilizaremos para convertir de formato unix time a datetime, la columna t de la fecha en nuestr DataFrame AAPL_df. Para aplicar la función a cada valor de la columna t usamos la función apply, estudiada anteriormente. Presentamos un ejemplo para que podamos visualizar el funcionamiento de la función date_format que recibirá como argumento un código unix time y lo transformará a datetime usando la función datetime.fromtimestamp()

def date_format(date_col):
    return datetime.fromtimestamp(date_col).strftime('%Y-%m-%d')
date_format(1602460800)
'2020-10-11'
AAPL_df['t'] = AAPL_df['t'].apply(date_format)
AAPL_df.head()
c h l o s t v
0 116.32 121.9900 116.05 120.50 ok 2020-11-08 154515315
1 115.97 117.5900 114.13 115.55 ok 2020-11-09 138023390
2 119.49 119.6300 116.44 117.19 ok 2020-11-10 112294954
3 119.21 120.5300 118.57 119.62 ok 2020-11-11 103350674
4 119.26 119.6717 117.87 119.44 ok 2020-11-12 81688586
  • Luego cambiamos los nombres de cada columna (iniciales) al nombre original que se utliza en los candlesticks de series de tiempo financieras, como se muestra en la siguiente figura. Eliminamos la columna s que no proporciona información relevante para las predicciones. Utilizaremos la función rename y drop ya usadas anteriormente con éste objetivo. Consideraremos para éste ejemplo el precio de cierre de la acción, debido a que es el más utlizado cuando deseamos tomar decisiones de inversión

  • La siguiente figura es extraida de tradingview, y representa el precio diario de las acciones de APPLE cotizadas en Nasdaq uno de los principales exchange de futuros y derivados in the Street. Distintas predicciones se pueden realizar por medio del uso de análisis técnico y análisis fundamental por ejemplo. En éste caso usaremos el modelo ARIMA como herramienta predictiva para horizontes cortos. Si desea experimentar un poco el análisis técnico realizado por aproximadamente un 90% de los traders, recomiendo usar Bitmex Testnet e iniciar con una wallet con Bitcoins de prueba (paper version), para adquirir destrezas antes de realizar alguna inversión, el análisis técnico es una técnica que requiere de muchos años de experiencia y disciplina para obtener resultados exitosos.

AAPL_df.rename(columns = {'c':'Close','h':'High','l':'Low','o':'Open','t':'Date','v':'Volume'}, inplace = True)
AAPL_df.drop('s', axis=1, inplace = True)
AAPL_df.head()
Close High Low Open Date Volume
0 116.32 121.9900 116.05 120.50 2020-11-08 154515315
1 115.97 117.5900 114.13 115.55 2020-11-09 138023390
2 119.49 119.6300 116.44 117.19 2020-11-10 112294954
3 119.21 120.5300 118.57 119.62 2020-11-11 103350674
4 119.26 119.6717 117.87 119.44 2020-11-12 81688586
  • Utilizaremos la función lineplot de seaborn para realizar un gráfico de la serie de tiempo de interés. Dado que el volumen de datos es amplio, para poder visualizar fechas en el eje \(x\) por ejemplo usamos la función get_xticks() para obtener estas fechas cambiar la frecuencia a ser colocada solo en la figura, para poder tener una visualización agradable a la vista. Dado que los nombres de cada etiqueta corresponden a strings de fechas, para evitar que queden uno sobre otro, rotamos los labels en éste eje usano la función plt.xticks(rotation=50), nótese que se ha colocado ; al final de éste llamado, ¿Con que objetivo?

plt.figure(figsize = (12,8))
ax = sns.lineplot(AAPL_df.Date, AAPL_df.Close)
freq = int(5)
xtix = ax.get_xticks()
ax.set_xticks(xtix[::freq])
plt.tight_layout()
plt.xticks(rotation=50);
_images/.~section2_155_0.png
  • Gráficos de velas en Python: El gráfico de velas es un estilo de gráfico financiero que describe la apertura, el máximo, el mínimo y el cierre para una coordenada \(x\) determinada (probablemente la hora). Los recuadros representan la dispersión entre los valores de apertura y cierre y las líneas representan la dispersión entre los valores bajos y altos. Los puntos de muestra en los que el valor de cierre es mayor (menor) que el de apertura se denominan crecientes (decrecientes). Por defecto, las velas crecientes se dibujan en verde mientras que las decrecientes se dibujan en rojo. Para realizar la figura usamos la función Figure() de la clase go de plotly, esta función recibe como input los datos asociados al candlestick suministrados por medio de la función Candlestick también de la clase go de plotly

import plotly.graph_objects as go

fig = go.Figure(data=[go.Candlestick(x = AAPL_df.Date,
                                     open = AAPL_df.Open, 
                                     high = AAPL_df.High,
                                     low = AAPL_df.Low, 
                                     close = AAPL_df.Close)
                     ])
fig.update_layout(
    title="Apple Inc. (AAPL)",
    xaxis_title="Day",
    yaxis_title="AAPL-USD",
    font=dict(
        family="Courier New, monospace",
        size=12,
        color="RebeccaPurple"
    )
)
fig.update_layout(xaxis_rangeslider_visible=False)
fig.show()
  • Utilizaremos ahora una prueba estadística para verificar si la serie de tiempo es estacionaria o no. Esta prueba es la de Dickey-Fuller. En cursos avanzados de series de tiempo se estudia estadísticamente éste tipo de pruebas así como los plots ACF y PACF, en esta sección sólo mencionaremos en una bastante resumida la idea detras de su uso. Para hacer uso de esta prueba importamos la función adfuller de la clase statsmodels.tsa.stattools. Para aplicar el test pasamos los datos del precio de cierre, serie de tiempo de interés, usando AAPL_df.Close. Éste será el argumento de la función adfuller encargada de realizar el test de Dickey-Fuller

from statsmodels.tsa.stattools import adfuller
from numpy import log
result = adfuller(AAPL_df.Close)
print('ADF Statistic: %f' % result[0])
print('p-value: %f' % result[1])
ADF Statistic: -1.615409
p-value: 0.475137
  • Dado que el valor crítico, el estadístico ADF =-1,8 > -2,5,-3,4,-2,8 (valores \(t\) en intervalos de confianza de 1%,5% y 10%), se acepta la hipótesis nula \((H_{o})\)

  • Valor \(p-\)= 0,480737 > 0,05. Dado que el valor \(p\) de Adfuller es mayor que un determinado valor alfa especificado (5%, nivel de significación o intervalo de confianza del 95%), se acepta la hipótesis nula \((H_{o})\), es decir, la serie temporal no es estacionaria

  • Realizaremos ahora figuras de autocorrelación para confirmar que la serie de tiempo es estacionaria, así como también para verificar cual es el orden de integración necesario para llevar nuestra serie de tiempo no estacionaria a una estacionaría. Para esto usaremos la función plot_acf de Python, la cual recibe como argumentos, la serie de tiempo de interés y el número de lags* que deseamos considerar en la figura. Nótese que utlizamos la función .diff() para realizar diferenciación en nuestra serie de tiempo, con el objetivo de remover tendencia, además nótese también que se eliminan valores nan que podamos obtener en éste proceso debido a que está figura consiste en graficar todos los valores \(\rho_{k}\) definidos anteriormente, donde aparece un cociente para el cual evitamos valores nulos en el denominador. Realizaremos tres figuras, correspondientes a la autocorrelación de la serie original, la serie diferenciada una vez, y dos veces. Colocaremos estas figuras en una matriz de \(3\times2\) usando la función fig, axes = plt.subplots(3, 2, sharex=True) de matplotlib. Para que cree que se usa el llamado sharex=True

import numpy as np, pandas as pd
from statsmodels.graphics.tsaplots import plot_acf
import matplotlib.pyplot as plt
plt.rcParams.update({'figure.figsize': (15,15)})

fig, axes = plt.subplots(3, 2, sharex=True)
axes[0, 0].plot(AAPL_df.Close); axes[0, 0].set_title('Original Series')
plot_acf(AAPL_df.Close, ax=axes[0, 1], lags = 240);

axes[1, 0].plot(AAPL_df.Close.diff()); axes[1, 0].set_title('1st Order Differencing')
plot_acf(AAPL_df.Close.diff().dropna(), ax=axes[1, 1], lags = 240);

axes[2, 0].plot(AAPL_df.Close.diff().diff()); axes[2, 0].set_title('2nd Order Differencing')
plot_acf(AAPL_df.Close.diff().diff().dropna(), ax=axes[2, 1], lags = 240);
_images/.~section2_164_0.png
  • Nótese el decaimiento geométrico en la primera figura de autocorrelación, que baja desde la parte positiva con una tendencia lineal, la cual es interpretada como una autocorrelación asociada a una serie de tiempo no estacionaría como indica la prueba de Dickey-Fuller que presenta efectivamente tendencias. Los intervalos de confianza se dibujan como un cono. Por defecto, se establece un intervalo de confianza del 95%. Al observar el gráfico de autocorrelación para la segunda diferenciación, el retardo entra en la zona negativa con bastante rapidez, lo que indica que la serie podría haber sido sobrediferenciada.

  • Definimos ahora nuestro conjuntos de entrenamiento y testeo para el modelo ARIMA, en éste caso nuestro test corresponde a los últimos 7 precios de las acciones de AAPL, y el train corresponde a la serie de tiempo original menos estos 7 días. El mejor modelo será obtenido mediante uso de los datos de entrenamiento y el criterio de Akaike y de inferencia Bayesiana el cual penaliza la suma de los residuos al cuadrado por incluir parámetros adicionales en el modelo, los modelos que tienen valores pequeños del AIC o BIC se consideran buenos modelos. El primer término del AIC o BIC es la función de verosimilitud para el modelo ajustado evaluada en las estimaciones de máxima verosimilitud de los parámetros desconocidos. En este contexto, AIC y SIC se denominan criterios de verosimilitud penalizados. En esta sección seleccionaremos el modelo con el menor AIC debido a que el AIC es asintóticamente eficiente (menor error de predicción en nuevos datos) pero el BIC no lo es. Más sobre esta técnica y el principio de máxima verosimilitud para selecciona de un buen modelo ARIMA, es abordado en cursos electivos de análisis de series de tiempo

\[AIC=\ln\left(\frac{\sum_{t=1}^{T}e_{t}^{2}}{T}\right)+\frac{2p}{T},\quad BIC=\ln\left(\frac{\sum_{t=1}^{T}e_{t}^{2}}{T}\right)+\frac{p\ln(T)}{T}\]
n_AAPL = len(AAPL_df.Close); n_test = 7 # This can be changed
train_size = n_AAPL - n_test

train = AAPL_df.Close.iloc[:train_size]
test_1w = AAPL_df.Close.iloc[train_size:train_size + n_test] 
dates_1w = AAPL_df.Date.iloc[train_size:train_size + n_test] 
print("train:", train.shape)
print("test_1w:", test_1w.shape)
train: (245,)
test_1w: (7,)
  • Utlizamos ahora el modelo ARIMA importado desde la librería statsmodels de Python para obetener distintos ARIMA de ordenes \(p,d,q\). Consideramos method = 'mle' para el cálculo de la verosimilitud exacta a través del filtro de Kalman. Como ejercicio puede reescribir estas líneas de código en una función que dependa sólo del input train y retorne los ordenes \(p, d, q\) asociados al criterio AIC de bondad de ajuste

from statsmodels.tsa.arima_model import ARIMA
import warnings
warnings.filterwarnings("ignore")
best_aic = np.inf
best_bic = np.inf

best_order = None
best_mdl = None

pq_rng = range(5)
d_rng  = range(3)

for i in pq_rng:
    for d in d_rng:
        for j in pq_rng:
            try:
                tmp_mdl = ARIMA(train, order=(i,d,j)).fit(method='mle', trend='nc')
                tmp_aic = tmp_mdl.aic                
                if tmp_aic < best_aic:
                    best_aic = tmp_aic
                    best_order = (i, d, j)
                    best_mdl = tmp_mdl
            except: continue
print('aic: {:6.5f} | order: {}'.format(best_aic, best_order))
aic: 1053.04714 | order: (3, 1, 3)
  • Existe una función llamada auto_arima de Python la cual es útli en ciertos casos especificos. Éste no es uno de ellos dado que el modelo ARIMA que entrega es un simple random walk \(x_{t}=x_{t-1}+\omega_{t}\), el cual predice puramente como un modelo estocástico con dependencia temporal basado totalmente en el punto temporal anterior \(t-1\)

import pmdarima as pm

model = pm.auto_arima(train, start_p=1, start_q=1,
                      test='adf',       # use adftest to find optimal 'd'
                      max_p=3, max_q=3, # maximum p and q
                      m=1,              # frequency of series
                      d=None,           # let model determine 'd'
                      seasonal=False,   # No Seasonality
                      start_P=0, 
                      D=0, 
                      trace=True,
                      error_action='ignore',  
                      suppress_warnings=True, 
                      stepwise=True)
Performing stepwise search to minimize aic
 ARIMA(1,1,1)(0,0,0)[0] intercept   : AIC=1055.416, Time=0.20 sec
 ARIMA(0,1,0)(0,0,0)[0] intercept   : AIC=1052.055, Time=0.01 sec
 ARIMA(1,1,0)(0,0,0)[0] intercept   : AIC=1053.830, Time=0.03 sec
 ARIMA(0,1,1)(0,0,0)[0] intercept   : AIC=1053.822, Time=0.03 sec
 ARIMA(0,1,0)(0,0,0)[0]             : AIC=1051.306, Time=0.01 sec

Best model:  ARIMA(0,1,0)(0,0,0)[0]          
Total fit time: 0.304 seconds
  • Por lo tanto para éste problema consideramos los mejores ordenes \(p, d, q\) obtenidos a a partir del criterio de Akaike. Los usamos como argumento de entrada en nuestro modelo ARIMA junto a nuestro train set, para obtener el modelo de ajustado de interés que utlizaremos para predecir valores futuros usando rolling

model = ARIMA(train, order=best_order)
model_fit = model.fit(method='mle', trend='nc')
  • Para graficar el ajuste de nuestro modelo ARIMA frente a nuestra serie de tiempo real utlizamos la función .plot_predict que proviene del objeto instanciado usando la función ARIMA

plt.figure(figsize = (12,8))
model_fit.plot_predict(dynamic=False)
plt.title("Real vs. ARIMA Forecast (Train)", fontsize=12)
Text(0.5, 1.0, 'Real vs. ARIMA Forecast (Train)')
<Figure size 864x576 with 0 Axes>
_images/.~section2_177_2.png
  • Para medir el error de predicción cometido en las predicciones, utilizaremos las métricas usuales en análisis de series de tiempo: MAPE, MAE, RMSE, MSE

def forecast_accuracy(forecast, actual, str_name):
    
    mape = np.mean(np.abs(forecast - actual)/np.abs(actual)) # MAPE
    mae = np.mean(np.abs(forecast - actual))                 # MAE
    rmse = np.mean((forecast - actual)**2)**.5               # RMSE
    mse = np.mean((forecast - actual)**2)                    # MSE
    
    df_acc = pd.DataFrame({'MAE': [mae],
                           'MSE': [mse],
                           'MAPE': [mape],
                           'RMSE': [rmse]},
                          index=[str_name])
    
    return df_acc
  • Realizamos ahora predicciones utlilizando rolling forecasting. El rolling forecast entrega reportes que utilizan datos históricos para predecir cifras futuras de forma continua durante un periodo de tiempo. Las previsiones continuas se utilizan a menudo en los reportes financieros, la gestión de la cadena de suministro, la planificación y la elaboración de presupuestos. El rolling forecast es una ayuda esencial para tomar decisiones empresariales acertadas. Gracias a su capacidad de respuesta, las previsiones continuas ayudan a las empresas a responder más rápidamente a las condiciones cambiantes del mercado. Si se utilizan con eficacia, las previsiones continuas pueden ayudar a identificar las deficiencias de rendimiento, acortar los ciclos de planificación y tomar la mejor decisión para los resultados. Para nuestros ejemplos, nuestro horizonte de predicción será de un día

def arima_rolling(history, test):
    
    predictions = list()
    for t in range(len(test)):
        model = ARIMA(history, order=best_order)
        model_fit = model.fit(method='mle', trend='nc')
        output = model_fit.forecast()
        yhat = output[0][0]
        predictions.append(yhat)
        obs = test[t]
        history.append(obs)
        print('predicted=%f, expected=%f' % (yhat, obs))
        
    return predictions
test_1wl = test_1w.tolist()
yhat_1w  = arima_rolling(train.tolist(), test_1wl)
predicted=152.662026, expected=149.800000
predicted=149.785056, expected=148.960000
predicted=148.636426, expected=150.020000
predicted=149.622634, expected=151.490000
predicted=151.060422, expected=150.960000
predicted=150.917737, expected=151.280000
predicted=151.328948, expected=151.200000
  • Utilizaremos ahora las funciónes creadas anteriormente para calcular el error de predicción y linpeplot para graficarle junto a la serie de tiempo orginal de test

forecast_accuracy(np.array(test_1wl), np.array(yhat_1w), "week 1")
MAE MSE MAPE RMSE
week 1 1.075665 2.061598 0.007137 1.435827
plt.figure(figsize = (8, 6))
sns.lineplot(dates_1w, yhat_1w, label="Forecast")
sns.lineplot(dates_1w, test_1wl, label="Test")
plt.tight_layout()
_images/.~section2_186_0.png

Tarea 2.2

  • Considere la serie de tiempo asociada con los futuros de la criptomoneda Bitcoin desde que comenzó a tradearse hasta la fecha del día de hoy. Utilice la API de finnhub para obtener esta serie de tiempo. Recuerde que para esto debe crear primero una cuenta, y así obtener su API Key para realizar la conexión de forma efectiva.

  • Repita TODOS los pasos indicados en esta sección para encontrar modelos ARIMA para predecir el precio de Bitcoin con los siguientes horizontes: 7 días, 14 días, 21 días, 28 días. Utilizar siempre predicciones usando rolling con ventana de predicción continua de un día. Cualquier cantidad de pasos extra para enriqucer su anális predictivo serán aceptados siempre y cuando sean acordes con lo que indica la teoría de análisis de series de tiempo.

  • Realice una tabla de error utlizando las métricas: MAPE, MAE, RMSE, MSE.

  • Repita el análisis desarrollado en los pasos anteriores considerando ahora el criterio de inferencia Bayesiana (BIC) para encontrar el mejor modelo ARIMA y compare los errores con aquellos obtenidos con el criterio de Akaike.

  • Escriba en cada paso las concluiones y análisis estadísticos asociados con los resultados obtenidos. Realice un test de normalidad para los residuales obtenidos para cada predicción, en cada caso agregue las correspondientes conlusiones estadísticas. Figuras y algoritmos que no estén acompañados de una conclusión, descripción y análisis estadístico, no serán tenidas en cuenta.

Errores comunes al crear visualizaciones interactivas

  • Objetivos

    • Identificar los errores que se cometen al crear visualizaciones

    • Aplicar técnicas para corregir los errores y crear visualizaciones eficaces

    • Seleccionar y diseñar las visualizaciones adecuadas para determinados tipos de datos

    • Describir las diferentes bibliotecas y herramientas disponibles para crear visualizaciones

Introducción

  • En éste capítulo se enumera y explica los posibles fallos y errores que se cometen durante varias etapas del proceso de visualización de datos, como la visualización de elementos no correlacionados de un conjunto de datos para mostrar una relación o crear una característica interactiva inapropiada

  • El proceso de visualización de datos puede parecer sencillo: tome algunos datos, trace algunos gráficos, añadir algunas características interactivas, y el trabajo está hecho. O, tal vez, no lo sea: podrían haber varios lugares durante el trayecto en los que se pueden cometer errores. Estos errores terminan por dar lugar a una visualización defectuosa que no puede de manera fácil y eficaz transmitir lo que dicen los datos, lo que hace que sea completamente inútil para el público que lo está viendo.

Formato e interpretación de datos

  • La primera fase de la visualización de datos es entender los datos frente a ti: entender lo que es, lo que significa y lo que transmite. Sólo cuando entiendas los datos serás capaz de diseñar una visualización que ayude a otros a entenderlos.

  • Además, es importante asegurarse de que los datos tienen sentido y contienen suficiente información, ya sea categórica, numérica o una mezcla de ambas, para ser visualizados. Por lo tanto, si tratas con datos erróneos o sucios, es probable que la visualización sea defectuosa.

  • En esta siguiente sección, veremos algunas formas de evitar los errores comunes que se suelen cometidos en esta fase de los datos y cómo evitarlos.

¿Cómo evitar los errores más comunes al tratar con datos sucios?

  • Garbage In, Garbage Out - este es un dicho popular en el campo de la ciencia de datos, especialmente con respecto a la visualización de datos. Básicamente significa que si utilizas datos desordenados y ruidosos, vas a obtener una visualización defectuosa y poco informativa. Los datos desordenados, ruidosos y sucios corresponden a una serie de problemas que se encuentran en los datos. Vamos a discutir los problemas uno por uno y las formas de tratar este tipo de datos.

Valores atípicos

  • Los datos que contienen valores inexactos o instancias que son significativamente diferentes del resto de los datos de un conjunto de datos se denominan valores atípicos. Estos valores atípicos pueden ser auténticos, es decir, parecen incorrectos pero en realidad no lo son, o son errores que se cometen al recoger o almacenar los datos. Veamos un ejemplo de un error cometido al recoger o almacenar los datos. La siguiente tabla muestra la edad, el peso y el sexo de los clientes que acuden a un determinado gimnasio. La columna de sexo consta de tres valores discretos 0, 1 y 2 que corresponden todos a una clase: hombre, mujer y otro, respectivamente. La columna de la edad se expresa en años y la columna de peso está en kilogramos

  • Todo parece correcto hasta que llegamos a la cuarta instancia (índice 3), donde el peso peso es de 790 kg. Esto parece extraño porque nadie puede pesar realmente 790 kg, especialmente alguien cuya estatura es de 1,5 metros y 7 pulgadas. Quien haya almacenado estos datos debe haber querido decir 79 kg y haber añadido un 0 por error. Este es un caso de un valor atípico en el conjunto de datos. Esto puede parecer trivial en este momento, sin embargo, esto puede resultar en visualizaciones defectuosas, y predicciones o patrones de modelos de aprendizaje automático, especialmente si hay múltiples repeticiones de esos datos. Ahora, veamos un ejemplo de un auténtico valor atípico en la siguiente tabla

  • El peso en el cuarto caso (índice 3) es de 167 kilogramos, lo que parece extrañamente alto. Sin embargo, sigue siendo un valor verosímil, ya que es posible que alguien tenga un peso de 167 kilogramos a los 37 años. Por lo tanto, se trata de un un verdadero valor atípico. Mientras que en los ejemplos anteriores es fácil detectar el valor atípico, ya que sólo hay 5 casos, en realidad, nuestros conjuntos de datos son masivos, por lo que comprobar cada caso es una tarea tediosa y poco práctica. Por lo tanto, en la vida real, podemos utilizar visualizaciones estáticas básicas, como los gráficos de caja, para observar la existencia de valores atípicos. Los gráficos de caja son visualizaciones de datos sencillas pero informativas que pueden decirnos mucho sobre la sobre la forma en que se distribuyen nuestros datos. Muestran el rango de nuestros datos basándose en cinco valores clave:

    • El valor mínimo de la columna

    • El primer cuartil

    • La mediana

    • El tercer cuartil

    • El valor máximo de la columna

  • Esto es lo que hace que sean excelentes para mostrar los valores atípicos, además de describir la simetría de los datos, el grado de agrupación de los mismos (si todos los valores están repartidos en un amplio rango), y si están o no sesgados.

Ejercicio 52: Visualización de valores atípicos en un conjunto de datos con un gráfico de cajas

  • En este ejercicio, vamos a crear un gráfico de caja para comprobar si nuestro conjunto de datos contiene valores atípicos. Vamos a utilizar el conjunto de datos gym.csv, que contiene información sobre los clientes de un determinado gimnasio.

  • Importar las librerías necesarias

import pandas as pd
import numpy as np
import plotly.express as px
  • Guarda el archivo gym.csv en un DataFrame llamado gym, e imprime las cinco primeras filas del mismo para ver cómo son los datos

gym = pd.read_csv('https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/gym.csv')
gym.head()
age weight sex
0 29 88 2
1 45 96 1
2 35 91 0
3 37 790 1
4 27 62 0
  • Como puede ver, nuestros datos tienen tres columnas: edad, peso y sexo. La columna sexo consta de tres valores discretos que corresponden a tres clases discretas - 0 es hombre, 1 es mujer y 2 es otro.

  • Cree un gráfico de caja con el eje \(x\) como columna de sexo y el eje \(y\) como peso

fig = px.box(gym, x = 'sex', y = 'weight', notched = False)
fig.show()
  • La escala del eje \(y\) es extrañamente grande, ya que todos los gráficos de caja están comprimidos en la octava parte inferior del gráfico, por lo que no representa una visualización clara de los datos. Esto se debe al valor atípico en la cuarta instancia de nuestro DataFrame 790 kg

  • Todos los valores parecen estar bien, excepto ese valor atípico en la parte superior del gráfico con max=790. Ahora, veremos las formas de tratar los valores atípicos.

Cómo tratar los valores atípicos

Hay tres formas principales de tratar los valores atípicos:

  • Eliminación: Si sólo hay unas pocas instancias (filas) que poseen valores atípicos, entonces esas instancias pueden ser eliminadas completamente del conjunto de datos, dejando así un conjunto de datos con cero valores atípicos. También hay ocasiones en las que una determinada característica (columna) contiene un gran número de valores atípicos. En tal caso, esa característica concreta puede eliminarse del conjunto de datos pero sólo si esa característica es insignificante. Sin embargo, la eliminación de datos no siempre es la mejor idea.

  • Imputación: La imputación es una opción mejor que la eliminación, especialmente si hay muchos valores atípicos en el conjunto de datos. Esto puede hacerse de tres maneras:

    • La forma más común es imputar los valores atípicos con la media, la mediana o la moda de la columna. Sin embargo, en el caso de muchos valores atípicos, estos valores pueden no ser lo suficientemente bueno, ya que cada valor atípico será un problema, debido a que cada uno de estos valores será sustituido por el mismo valor (la media, la mediana o la moda)

    • El otro método para obtener mejores valores para los valores atípicos, especialmente en el caso de de series temporales, es la interpolación lineal, es decir, el uso de polinomios lineales para crear nuevos puntos de datos dentro de un rango definido de puntos conocidos para reemplazar valores atípicos.

    • Un modelo de regresión lineal también puede utilizarse para predecir un valor que falta si es numérico, y en el caso de que el valor que falta sea categórico, se puede utilizar un modelo de regresión logística.

    • Por ejemplo, supongamos que tiene un conjunto de datos del que necesita mostrar una relación entre la altura y el peso. La columna de la altura tiene varios valores perdidos pero, como es una característica significativa, no puede eliminarla, ni tampoco puede imputar la media de la columna ya que eso podría llevar a una relación falsa. El conjunto de datos puede dividirse en dos conjuntos de datos

      • El conjunto de datos de entrenamiento, que contiene instancias sin valores perdidos

      • El nuevo conjunto de datos, que contiene sólo los casos en los que faltan valores en la columna de altura.

      • A continuación, se puede utilizar un modelo de regresión lineal en el conjunto de datos de entrenamiento. El modelo aprenderá de estos datos y, cuando se le proporcione el nuevo conjunto de datos, podrá predecir los valores de la columna de altura. Ahora, los dos conjuntos de datos pueden fusionarse juntos y ser utilizados para crear visualizaciones ya que no hay valores perdidos.

  • Transformación: Es el proceso de transformación de los valores atípicos mediante la construcción de la columna de datos en la que se encuentra el valor atípico, por ejemplo, convirtiendo los valores en porcentajes y utilizando esa columna como característica en lugar de la columna original. En la siguiente sección, veremos un ejercicio para entender cómo podemos tratar los los valores atípicos.

Ejercicio 53: Cómo tratar los valores atípicos

Detección de valores atípicos

  • Si nuestro conjunto de datos es pequeño, podemos detectar el valor atípico simplemente mirando el conjunto de datos. Pero si tenemos un conjunto de datos enorme, ¿cómo podemos identificar los valores atípicos? Tenemos que utilizar técnicas de visualización y matemáticas. A continuación se presentan algunas de las técnicas de detección de valores atípicos

    • Boxplots

    • Puntuación \(Z\)

    • Rango intercuantil (IQR)

Detección de valores atípicos mediante las puntuaciones \(Z\)

  • Criterio: Cualquier dato cuya puntuación \(Z\) esté fuera de la tercera desviación estándar es un valor atípico

Implementación:

  • Recorrer todos los datos y calcular la puntuación \(Z\) mediante la fórmula \((X_{i}-\mu)/\sigma\). Definir un valor de umbral de 3 y marcar los puntos de datos cuyo valor absoluto de puntuación \(Z\) sea mayor que el umbral como valores atípicos.

import numpy as np
outliers = []
def detect_outliers_zscore(data):
    thres = 3
    mean = np.mean(data)
    std = np.std(data)
    for i in data:
        z_score = (i - mean)/std
        if (np.abs(z_score) > thres):
            outliers.append(i)
    return outliers
sample_outliers = detect_outliers_zscore(gym.weight)
print("Outliers from Z-scores method: ", sample_outliers)
Outliers from Z-scores method:  [790]

Detección de valores atípicos mediante el rango intercuantil (IQR)

  • Criterio: Los datos que se sitúan 1.5 veces del IQR por encima de \(Q_{3}\) y por debajo de \(Q_{1}\) son valores atípicos.

Implementación

  • Ordenar el conjunto de datos de forma ascendente

  • Calcular los cuartiles 1 y 3 \((Q_{1}, Q_{3})\)

  • Calcular \(IQR=Q_{3}-Q_{1}\)

  • Calcular el límite inferior = \((Q_{1}-1.5*IQR)\), el límite superior = \((Q_{3}+1.5*IQR)\), recorrer los valores del conjunto de datos y comprobar los que están por debajo del límite inferior y por encima del límite superior y marcarlos como valores atípicos

outliers = []
def detect_outliers_iqr(data):
    data = sorted(data)
    q1 = np.percentile(data, 25)
    q3 = np.percentile(data, 75)
    IQR = q3-q1
    lwr_bound = q1-(1.5*IQR)
    upr_bound = q3+(1.5*IQR)
    for i in data: 
        if (i<lwr_bound or i>upr_bound):
            outliers.append(i)
    return outliers
sample_outliers = detect_outliers_iqr(gym.weight)
print("Outliers from IQR method: ", sample_outliers)
Outliers from IQR method:  [790]

Tratamiento de los valores atípicos

Eliminación

  • En este ejercicio, vamos a eliminar la instancia que contiene el valor atípico del conjunto de datos que utilizamos en el Ejercicio 52, y visualizamos el conjunto de datos de nuevo generando un gráfico de caja basado en el nuevo conjunto de datos.

  • Importar las librerías necesarias

import pandas as pd
import numpy as np
import plotly.express as px
  • Guarda el archivo gym.csv en un DataFrame llamado gym, e imprime las cinco primeras filas del mismo para ver cómo son los datos

gym = pd.read_csv('https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/gym.csv')
gym.head()
age weight sex
0 29 88 2
1 45 96 1
2 35 91 0
3 37 790 1
4 27 62 0
  • Modificar el DataFrame del gimnasio para que sólo esté formado por los casos en los que el peso sea inferior a 103 e imprimir las cinco primeras filas:

gym_del = gym[gym.weight < 104]
gym_del.head()
age weight sex
0 29 88 2
1 45 96 1
2 35 91 0
4 27 62 0
5 58 55 0
  • Vamos a crear un boxplot para ver el aspecto de los datos

fig1 = px.box(gym_del, x = 'sex', y = 'weight', notched = True)
fig1.show()

Imputación por medio de la media, mediana o moda

  • Como el valor medio es sensible a valores atípicos, se aconseja sustituirlos por la mediana

median = np.median(gym.weight)
print("median: ", median)

for outlier in sample_outliers:
    gym.weight = np.where(gym.weight == outlier, median, gym.weight)
median:  76.0
gym.head()
age weight sex
0 29 88.0 2
1 45 96.0 1
2 35 91.0 0
3 37 76.0 1
4 27 62.0 0
fig1 = px.box(gym, x = 'sex', y = 'weight', notched = True)
fig1.show()

Ejercicio 54: Tratamiento de los valores faltantes

Datos faltantes

  • Los datos perdidos son, como su nombre indica, valores que están en blanco (NaN, -, 0 cuando no deberían ser 0, etc.). Al igual que los valores atípicos, los valores perdidos pueden ser problemáticos tanto en el caso de las visualizaciones como en el de los modelos predictivos.

  • Los valores faltantes en las visualizaciones pueden mostrar una tendencia que en realidad no existe o no representa una relación entre dos variables que, en realidad, es significativa. Aunque es posible crear visualizaciones con un conjunto de datos que contenga valores perdidos, no se recomienda hacerlo. Al hacerlo, se ignoran los casos en los que se encuentran esos valores perdidos, creando así una visualización basada en algunos de los datos pero no en todos.

  • Por lo tanto, el tratamiento de los valores perdidos es de suma importancia. Existen dos enfoques principales para tratar los valores perdidos: la supresión y la imputación, ambos discutidos en términos de tratamiento de los valores atípicos. La La misma lógica se aplica a los valores perdidos.

  • En este ejercicio, vamos a trabajar con un conjunto de datos que tiene siete valores perdidos en forma de 0s. En primer lugar, eliminaremos las instancias que contienen estos valores perdidos y generar un gráfico de caja para ver el impacto que la eliminación de un gran número de instancias en nuestra visualización. A continuación, imputaremos el valor de la mediana de la columna que que contiene los valores perdidos y generaremos un gráfico de caja basado en este conjunto de datos imputados.

  • Importar las librerías necesarias

import pandas as pd
import numpy as np
import plotly.express as px
  • Guarda el archivo weight.csv en un DataFrame llamado weight, e imprime las cinco primeras filas del mismo para ver cómo son los datos y utiliza la función .describe() para mostrar información sobre ella

w = pd.read_csv('https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/weight.csv')
w.head()
weight sex
0 47 2
1 0 1
2 35 0
3 34 1
4 0 0
w.describe()
weight sex
count 62.000000 62.000000
mean 33.887097 0.838710
std 15.683451 0.813685
min 0.000000 0.000000
25% 25.000000 0.000000
50% 35.000000 1.000000
75% 46.000000 1.750000
max 56.000000 2.000000
  • Como podemos ver, el valor de peso mínimo en nuestro conjunto de datos es 0; sin embargo, nadie puede pesar 0 kgs, lo que significa que tenemos valores faltantes en forma de 0s. Intentemos eliminar estas instancias.

  • Cree un nuevo DataFrame que conste sólo de las instancias en las que el peso no sea igual a 0. Muestre información sobre este nuevo DataFrame

doc_w = w[w.weight != 0]
doc_w.describe()
weight sex
count 55.00000 55.000000
mean 38.20000 0.836364
std 10.49056 0.811118
min 21.00000 0.000000
25% 31.00000 0.000000
50% 36.00000 1.000000
75% 46.50000 1.500000
max 56.00000 2.000000
  • Crea un boxplot con este nuevo DataFrame, con el eje \(x\) como sexo y el eje \(y\) como peso

fig1 = px.box(doc_w, x = 'sex', y = 'weight', notched = True)
fig1.show()
  • Ahora, el valor del peso mínimo es 21, lo que tiene más sentido. Sin embargo, nuestro conteo se ha reducido a 55 de 62, lo que significa que hemos eliminado 7 instancias de nuestro conjunto de datos. Esto puede parecer pequeño en este ejemplo, pero en realidad, esto puede tener repercusiones serias en la información obtenida.

  • Además, en el gráfico de caja anterior, el extremo inferior de la caja para el sexo 0 y el extremo superior de la caja para el sexo 2 es ligeramente anormal. Por lo tanto, sustituyamos los valores 0 de la columna de peso por el valor medio de la columna. Recuerde que tenemos que calcular la media de la columna ¡columna sin tener en cuenta esos valores 0! Si los tenemos en cuenta, entonces nuestra media será incorrecta.

  • Calcular la media de la columna de pesos del DataFrame que consiste en sólo valores de peso distintos de cero

mean_w = doc_w['weight'].mean()
mean_w
38.2
  • Utilice la función .replace() para sustituir los valores 0 presentes en la columna weight del DataFrame original por la media de la columna weight del modificado. Guarde esto en un nuevo DataFrame

w_new = w.replace({'weight': {0: mean_w}})
  • Muestra la información del nuevo DataFrame

w_new.describe()
weight sex
count 62.000000 62.000000
mean 38.200000 0.838710
std 9.870307 0.813685
min 21.000000 0.000000
25% 31.250000 0.000000
50% 38.100000 1.000000
75% 46.000000 1.750000
max 56.000000 2.000000
  • Nuestro recuento es de 62, lo que significa que tenemos todas las instancias, y nuestro peso mínimo es 21, lo que significa que no tenemos ningún 0.

  • Cree un gráfico de caja con este nuevo DataFrame, con el eje \(x\) como sex y el eje \(y\) como weight.

fig2 = px.box(w_new, x = 'sex', y = 'weight', notched = True)
fig2.show()
  • Ahora, tenemos una visualización que no tiene valores perdidos y representa todas las instancias que están presentes en el conjunto de datos. Veamos el tercer problema que puede generar una visualización defectuosa.

Instancias y/o funciones duplicadas

  • El tercer problema es la presencia de instancias y/o características duplicadas en un conjunto de datos. Se trata de elementos innecesarios en el conjunto de datos y, si no se eliminan, pueden pueden afectar a las tendencias y a los conocimientos que se muestran en una visualización.

  • Por ejemplo, puede crear una visualización que muestre la relación entre el género de un adolescente y si toca el piano. Con un conjunto de datos sin valores atípicos, anomalías o valores perdidos, obtendrá una gran visualización. A partir de la visualización, podrá también podrá concluir que hay más mujeres que tocan el piano que hombres. Sin embargo, digamos que la visualización proviene del conjunto de datos siguiente

  • Hay dos instancias para Nita Thadaka y tres instancias para Pooja Rajesh, lo que significa que hay tres instancias duplicadas en total. Esto significa que la información que la visualización está proporcionando es inexacta. La forma de tratar los duplicados es sencilla: eliminarlos. Con este fin puede utlizar la función: .drop_duplicates()

Mala selección de características

  • Con respecto a un conjunto de datos, una característica es una columna en el conjunto de datos, mientras que una instancia es una fila en el conjunto de datos. Por ejemplo, en la tabla anterior, nombre, sexo, tocar el piano y la edad son características, mientras que Pooja Rajesh, F, Yes y 17 es una instancia.

  • Dado que el objetivo de una visualización es mostrar una tendencia, un patrón, una relación o algún vínculo entre dos o más características en un conjunto de datos, es importante que la selección de esas características se haga con cuidado. Por lo tanto, este es un punto crucial en la visualización de datos de datos. Si el objetivo es transmitir que existe una fuerte relación entre dos características, entonces hay que Si el objetivo es transmitir que existe una fuerte relación entre dos características, hay que asegurarse de que están fuertemente correlacionadas antes de proceder a su visualización. La selección de características insignificantes dará lugar a una visualización sin sentido y no acabará no transmitirá ninguna información concreta. Por ejemplo, en cuanto al conjunto de datos co2.csv, el conjunto de datos contiene información sobre las emisiones de dióxido de carbono por país y el PIB por país. Comprobamos la correlación entre las emisiones de CO2 y el PIB antes de visualizar el conjunto de datos, garantizando que íbamos a crear una una visualización que valiera la pena.

Tarea 2.3

  • Determine las características que se visualizan en un gráfico de dispersión. Se le da el conjunto de datos co2.csv y se le pide que proporcione información sobre él, como por ejemplo, qué tipo de patrones existen, si hay tendencias entre las características, etc. Es necesario que su visualización final transmita información significativa. Para conseguirlo, va a crear visualizaciones para diferentes emparejamientos de características para entender están correlacionadas y, por lo tanto, vale la pena visualizarlas.

Pasos principales

  • Importe las bibliotecas necesarias.

  • Vuelva a crear el DataFrame. Desde el DataFrame gm incluya las columnas population, fertility, y life

  • Visualiza la relación entre el co2 y life utilizando un gráfico de dispersión, con el nombre del país como información en la herramienta hover y el año como deslizador.

  • Comprueba la correlación entre co2 y life.

  • Visualiza la relación entre co2 y fertility mediante un gráfico de dispersión, con el nombre del país como información en la herramienta hover y el año como deslizador.

  • Comprueba la correlación entre co2 y fertility

Importación y cargue de datos en PostgreSQL usando Heroku

Objetivos

  • Crear un base de datos en Heroku Postgres y verificar conexión y e importación de tablas usando pgAdmin de PostgreSQL

  • Utilizar alternativas proporcionadas por el cliente de Heroku para solucionar el problema de las 20 conexiones

  • Utilizar la API de Python para PostgreSQL para importar datos desde un archivo CSV a Heroku Postgres y luego realizar consultas a nuestras tablas creadas y estudciar la forma de realizar cálculos especificos mediante consultas SQL

  • Generar visualizaciones a partir de las tablas creadas

Introducción

  • Heroku es una empresa que se especializa en ofrecer servicios de plataforma administrada, por sus siglas en inglés PasS, en otras palabras ofrece servicios de servidores y redes administrados por Heroku en donde se pueden alojar aplicaciones de diferentes lenguajes de programación como Python, Java, PHP y más. Los servicios que Heroku ofrece están claramente orientados para desarrolladores, startups y empresas pequeñas. Heroku nació en el 2007 y fue adquirida en el 2010 por salesforce.com.

  • Heroku utiliza contenedores Linux (Ubuntu) los cuales son llamados “dynos”, estos son utilizados para alojar las aplicaciones web, webservices o aplicaciones que se ejecutan del lado del servidor, así mismo cuenta con la posibilidad de instalar add-ons para agregar funcionalidades a dichos contenedores, por ejemplo, se pueden agregar servicios administrados de base de datos, almacenamiento en la nube o mensajería(MQ) por mencionar algunos.

  • Heroku tiene su clientela bien definida: empresas que quieren dejar de preocuparse por cuestiones de infraestructura y sólo enfocarse en el desarrollo. Por lo general estas suelen ser empresas grandes o startups que prefieren no invertir en un equipo de operaciones cuando están en una etapa temprana, y su prioridad debe ser hacer un producto que las personas quieran.

  • Heroku tiene dos tiers, o niveles, para personas interesadas en aprender: una versión gratuita similar a la de now.sh, que entra en modo “sleep” cada 30 minutos sin tráfico, y otra de 7 USD que compite con el servicio básico de 5 USD al mes que ofrece Digital Ocean, pero agregar las ventajas de que nuestros servidores sean administrados por nosotros.

  • PostgreSQL es un sistema para gestionar bases de datos de muy alto nivel, completamente de software libre y con una licencia BSD, compatible con cualquier uso, ya sea personal o comercial. Este sistema tiene como precursor otro sistema gestor de bases de datos, llamado INGRES, que fue uno de los primeros intentos de implementar un sistema de bases de datos relacional. INGRES abrió el camino para muchos otros sistemas conocidos como Sybase, Informix o el propio SQL Server. El impulsor de INGRES lideró posteriormente también el desarrollo de PostgreSQL, cuyo nombre del proyecto hace referencia a su propia raíz (Post-Ingres).

  • PostgreSQL es un sistema considerado como empresarial. Tratándose de la aplicación de bases de datos más avanzado de código abierto, podría utilizarse por su funcionalidad y potencia como reemplazo de otras bases de datos comerciales, incluso del poderoso Oracle. Hasta cierto punto, resulta extraño que no sea el más popular, condición que ostenta MySQL.

  • PostgreSQL tiene dos ventajas fundamentales, primero en lo que respecta a su funcionalidad y capacidad de trabajar con mayores cantidades de datos, pero también en lo que respecta a su licencia. MySQL tiene una licencia dual, lo que significa que para proyectos comerciales habría que pagar por su uso. Sin embargo, PostgreSQL tiene una única licencia totalmente abierta para cualquier uso. Algunas de sus principales características son: Alta concurrencia, Soporte para múltiples tipos de datos de manera nativa, Soporte a triggers, Trabajo con vistas, Objeto-relacional, Soporte para bases de datos distribuidas, Soporte para gran cantidad de lenguajes como C, C++, Java, PHP o Python. Además, ofrece interfaces para ODBC y JDBC, así como interfaces de programación para infinidad de lenguajes de programación.

Creación de base de datos Heroku Postgres

  • Antes de seguir con los siguientes pasos debe tener isntalado PostgreSQL. El objetivo en esta sección es, luego de crear la base de datos en la nube Heroku Postgres, realizaremos conexiones a esta misma utlizando pgAdmin con el objetivo de verificar que ésta fue creada exitosamente. Para instalar PostgreSQL puede seguir cada uno de los siguientes tutoriales, seleccionados de los muchos que puede encontrar en la red. Si se siente a gusto con algún otro tutorial en el que realicen el mismo procedimiento, adelante. Sólo tenga en cuenta, agregar todas las componentes de PostgreSQL en el proceso de instalación

  • Una vez tiene isntalado PostgreSQL, procedemos con la creación de la base de datos en Heroku. Heroku Postgresql requiere de la creación de una app inical, para poder crear la base de datos. No es necesario aún desplegar su aplicación Dash o Shiny en Heroku, sólo usaremo la nube para crear nuestra base de datos y cargar nuestras tablas. Primero que todo acceda al sitio web de Heroku, haga click en la parte superior derecha Sign up y cree una cuenta

  • Creada la cuenta en Heroku procedemos a la creación de una app para poder crear la base de datos. La app es la unidad fundamental de organización en Heroku. Cada app puede estar asociada a su propio conjunto de complementos provisionados. Hacemos click en Create new app en la pagina inicial en nuestra, al ingresar a nuesta cuenta en Heroku

  • Crea una app con el nombre de tu preferencia. La plataforma no acepta nombres con guión bajo, pero si al medio. Podrás crear otra luego de este primer test otra app, sólo trata esta primera vez de seguir cuidadosamente cada paso.

  • Luego de realizar éste procedimiento visualizarás la app de Heroku en la siguiente forma. En éste lugar puedes desplegar también tu app de Dash o Shiny que estudiaremos en la siguiente sección. Esta vez, sólo utilizaremos la app para crear una instancia para nuestra base de datos. Haga click en Resources con este objetivo

  • Luego le va a aparecer la siguiente ventana. Debe hacer click en Find more add-ons. Procedemos a instalar el addon para instalar la base de datos basada en PostgreSQL. Aparecerá una lista de varios addons que no usaremos en este momento, seleccionaremos de la lista a Heroku Postgres

  • Asegurese de que está instalando la versión gratuita de Heroku Postgres. Debe aparecer el mensaje que vemos en pantalla Starting at 0/mo y más aabajo Hobby Dev Free. Nótese también que en esta página aparece la capacidad de almacenamiento, número máximo de filas que debe tener la tabla que importemos en PostgreSQL y además el número de conexiones, al cual buscaremos una solución alternativa, mas adelante. Haga click en Install Heroku Postgres y asegurese de estar logeado con tu cuenta

  • Luego de realizar la instalación, le aparecerá la siguiente pantalla en la que deberá seleccionar la app que creamos anteriormente. Haga click en Submit Order Form para finalizar la creación de su base de datos y espere unos segundos

  • Exploremos ahora la base de datos que hemos creado. Nótese que el costo estimado mensual es 0. Efectivamente esto ocurre porque hemos seleccionado la versión gratuita Hobby Dev - Free. Hagamos click en Heroku Postgres para buscar la información de conexión a nuestra nueva base de datos

  • Por ahora no veremos ninguna conexión, hasta que comencemos a usar la la base de datos. Optaremos por usa pgAdmin a manera de test, con el objetivo principal de verificar que podemos realizar conexiones a nuestra base de datos Heroku Postgres.

  • Haga click en Settings y View Credentials para poder acceder a la información necesaria para la conexión a la base de datos. Estos datos son los que usaremos mas adelante para conectarnos a esta base de datos con pgAdmin de PostgreSQL y también desde nuestro notebook de Python, utlizando la API encargada de esto. La información en el recuadro es la más importante, pues es la que usaremos para realizar nuestra conexión a nuestra base de datos

pgAdmin PostgreSQL

  • Ahora realizaremos una prueba de conexión utilizando pgAdmin, la cual viene incluida en la instalación de PostgreSQL. Inice la aplicación, le pedirá la contraseña que utlizó durante la instalación. Vamos a crear un servidor remoto el cual usaremos para conectarnos a la base de datos Heroku Postgres. Haga click derecho en Servers luego Create/Server como en la siguiente imágen

  • Coloque el nombre de su preferencia al servidor que usaremos para conectarnos a la base de datos. Usted puede crear una base de datos desde su computadora. Su computadora funcionaría como un servidor local, a diferencia de Heroku que es un servidor en la nube. Por otro lado también puede crear Dockers para desplegar aplicaciones. Un Docker es un sistema operativo (o runtime) para contenedores. El motor de Docker se instala en cada servidor en el que desee ejecutar contenedores y proporciona un conjunto sencillo de comandos que puede utilizar para crear, iniciar o detener contenedores. Heroku usa Dockers para el funcionamiento de sus servidores. Aunque Heroku facilita mucho la creación de base de datos y el despliegue de aplicaciones, con Docker obtienes infinitamente más flexibilidad y portabilidad porque el usuario controla el contenedor subyacente en lugar de tenerlo definido por Heroku. Cual usar va a depender de sus necesidades

  • Dirijase a la casilla Connection y coloque en Host name/address, el Host de Heroku Postgres. Mantenga el Port como 5432. Un puerto es esa interfaz a través de la cual se pueden enviar y recibir los diferentes tipos de datos. En la casilla Maintenance database colocaremos el nombre de nuestra base de datos en Heroku que aparece como Database. En la casilla Username colocaremos el nombre de usuario en Heroku que aparece como User. Luego colocamos el Pasword llamado de la misma forma en pgAdmin y Heroku Postgres. Debe verse la casilla Connection como se ve a continuación

  • Además de esto debe colocar el nombre de la base de datos creada en Heroku denotado como Database en la casilla Advanced. Esto lo hacemos para poder conectarnos a nuestra base de datos y no a una de las tantas gratuitas, creadas por otros usuarios en el servidor ofrecido por Heroku. Al final en pgAdmin haga click en Save

  • Luego de esto podrá ver que se ha creado el servidor en pgAdmin con el nombre indicado. Ahora estamos listos para probar conexión a la base de datos en Heroku utlizando éste nuevo servidor

  • Para verificar que se ha conectado a su base de datos en Heroku Postgres, haga click en overview y vera que el número de conexiones ha cambiado

  • Pasemos ahora a crear un tabla de prueba en la base de datos desde pgAdmin para confirmar conexión y además visualizar el cambio en el número de tablas desde Overview. Haga click desde pgAdmin en el servidor creado con su nombre de preferencia, en este ejemplo HerokuServer, luego haga click en Tools/Query Tool para realizar una consulta a nuestra base de datos en Heroku Postgres

  • Verifique que en la parte superior aparece la base de datos correcta a la que estamos conectados. Creamos luego una tabla llamada accounts con user_id y name, para verificar que todo funciona correctamente. Seleccione el código para crear la tabla y luego presionamos el botón de Run para ejecutar el Query. Debe aparecer el mensaje de consulta exitosa automáticamente. Profundizaremos en la proxima sección acerca del significado de estas consultas

  • Haga click luego en Schemas/Tables y vera que fue creada la tabla accounts en la base de datos Heroku Postgres. Nótese que en Heroku aparecerán 2 conexiones y la tabla que fue creada automáticamente en TABLE

  • Realicemos ahora un nuevo registro en la tabla creada, el cual vamos a poder evidenciar en overview con el ítem ROW. Utilizaremos las consultas qeu observa en la siguiente imágen. INSERT INTO es del lenguaje SQL y es utilizada para insertar datos en la tabla creada. SELECT * FROM es utlizada para seleccionar columnas de nuestra base de datos, en éste caso usamos * para indicar que queremos seleccionar y a su vez visualizar los datos en la tabla. Nótese que ya aparece actualizado en Heroku Postgres el ítem ROW con el nuevo registro o fila agregada a la tabla

  • Cuando regrese al inicio, o ingrese nuevamente a Heroku aparecerá en la primera venta la appque contiene la base de datos. Podrá aceder a ella las veces que lo desee para verificar el número de conexiones y que se hayan importado las tablas correctamente

  • Habrá notado que el número de conexiones a Heroku Postgres está límitado a 20 conexiones, esto suele ser un problema cuando contamos con aplicaciones que a menudo para su correcto funcionamiento realizan múltiples consultas a la base de datos. Con el fin de resolver éste problema usted puede construr un script que elimine el número de conexiones cada hora por ejemplo. La pregunta ahora sería, ¿Como eliminar estas 20 conexiones a Heroku Postgres?. Algunos desarrolladores con éste fin, utilizan heroku-pg-extras, esta librería puede ser utilizada para reiniciar el número de conexiones a Heroku Postgres. Esto va a requerir primero instalar el cliente de Heroku Heroku CLI. La instalación del cliente puede ser realizada en cualquier sistema operativo, en ésta sección a manera de ejemplo realizaremos la instalación en sistema opertativo Windows, y además porque cuenta con el mayo número de usuarios, un total de 1.5 billones, en comparación con los 100 millones de usuarios que usan Mac y 40 millones que usan Ubuntu. El procedimiento para el resto de sistemas operativos es bastante similar, pues todo sera ejecutado desde la línea de comandos. El link de descarga del cliente Heroku CLI lo llevara a la página de instalación, donde debe escoger el binario asociado con su sistema operativo

  • Mantenga todos los check por default durante la instalación de Heroku CLI, y haga click en Install

  • Luego que el procedimiento de instalación ha finalizado, verificamos que la instalación fue realizada con éxito. Para esto en nuestro Command Prompt o Windows PowerShell, vamos a ejecutar la siguiente orde: heroku version

  • Antes de usar el cliente debemos iniciarlo con nuestra cuenta de Heroku. Para esto utilizaremos el siguiente comando el cual nos permitirá inciar el cliente pasando antes por el inicio de sesión en nuestra cuenta de Heroku

  • Haga click en Log In para iniciar sesión desde el cliente de Herokude desión

  • En la ventana de comando podrá ver que ya ha iniciado sesión en el cliente de Heroku. Usando la orden heroku apps en nuestro línea de comandos podremos visualizar las apps que tenemos creadas, en este caso una sola, la que hemos usado en esta sección

  • Si aparece un mensaje de warning relacionado con la actualización del cliente, podemos hacer este procedimiento con la orden: heroku update

  • Ahora si puede instalar el plugin que permite matar todas las conexiones. Este plugin se utiliza para obtener información sobre una instancia de Heroku Postgres, que puede ser útil al analizar los problemas de rendimiento. Esto incluye información sobre los bloqueos, el uso de los índices, los ratios de éxito de la caché del buffer y las estadísticas de vacío. Para su instalación utlice la orden: heroku plugins:install heroku-pg-extras. Puede ignorar los mensajes de warning, pues estos no afectarán al funcionamiento de los heroku-pg-extras

  • Escriba heroku help pg para visualizar la forma de usar heroku-pg-extras en el cliente de Heroku

  • Siguiendo las intrucciones de uso de heroku-pg-extras podemos visualizar información acerca de la app creada en Heroku con la orden: heroku pg:info --app dataviz-lrubio-postgresql-test

  • Para matar todas las conexiones a Heroku y dejarlas en 0, para tener nuevamente las 20 posibles disponibles, debemos utlizar pg:killall. En el caso que tenga una app que realice muchas consultas, calcule cada cuanto completa por ejemplo las 20, por ejemplo, cada 5 minutos, y luego cree un script que se active automaticamente cada 5 minutos el cual se encarge de ejecutar pg:killall

  • Existen distintas opciones para evitar éste tipo de procedimiento. La primera de ellas es contratar el servicio mensual de Heroku. Algunos prefieren usar directamente AWS o Google Cloud, pero estos son pagos también, necesitarás usar tu tarjeta de crédito. Las versiones “gratuitas” de AWS te permiten crear bases de datos y desplegar apps, pero costos son generados siempre, incluso cuando apagas el servicio, automáticamente se activa cada 7 días generandote costos. Otra opción es usar por ejemplo Google Cloud, la cual es de agrado para muchos desarrolladores en la nube, aunque es también paga, te ofrece 3 meses gratuitos, los cuales puedes usar a tu gusto y luego decidir si mantienes o cierras tu cuenta. Por ejemplo para graficos con mapas usando ggmap, en la sección de R usaremos este plan gratuito para acceder a mapas de Google. También esta la opción de usar Docker directamente. Docker es un servicio de virtualización de aplicaciones dentro de unos denominados contenedores. Esto permite que las aplicaciones se ejecuten independientemente del host que los alberga. Si eres usuario de Windows o Mac necesitarás tener instalado Docker Desktop en tu pc o portátil y éste debe tener cierta capacidad y potencia. Por otro lado, en el caso de que seas usuario de Windows, deberás tener la versión Professional para poder instalar Docker. Si no es así, debe hacerlo desde Ubuntu.

Consultas desde Python a Heroku Postgres

  • PostgreSQL puede ser integrado con Python usando el módulo psycopg2. psycopg2 es un adaptador de base de datos PostgreSQL para el lenguaje de programación Python. psycopg2 fue escrito con el objetivo de ser muy pequeño, rápido y estable como una roca. No necesitas instalar este módulo por separado porque se entrega, por defecto, junto con la versión 2.5.x de Python en adelante. Sin embargo, si no lo tienes instalado en tu máquina, puedes usar los siguientes comandos según tu preferencia de gestor de paquetes

    • pip install psycopg2

    • conda install -c anaconda psycopg2

  • Para utilizar el módulo psycopg2, primero debe crear un objeto connection que represente la base de datos y luego, opcionalmente, puede crear un objeto cursor que le ayudará a ejecutar todas las sentencias SQL.

APIs del módulo Python psycopg2

Las siguientes son rutinas importantes del módulo psycopg2, que pueden satisfacer su necesidad de trabajar con la base de datos PostgreSQL desde su programa Python. Si usted está buscando una aplicación más sofisticada, entonces usted puede mirar en la documentación oficial del módulo Python psycopg2.

  • Esta API abre una conexión a la base de datos PostgreSQL. Si la base de datos se abre con éxito, devuelve un objeto de conexión

psycopg2.connect(database="testdb", user="postgres", password="cohondob", host="127.0.0.1", port="5432")
  • Esta rutina crea un cursor que se utilizará durante toda la programación de la base de datos con Python

connection.cursor()
  • Esta rutina ejecuta una sentencia SQL. La sentencia SQL puede ser parametrizada (es decir, marcadores de posición en lugar de literales SQL). El módulo psycopg2 soporta marcadores de posición utilizando el signo %s. Por ejemplo: cursor.execute("insert into people values (%s, %s)", (who, age))

cursor.execute(sql [, optional parameters])
  • Esta rutina ejecuta un comando SQL contra todas las secuencias de parámetros o mapeos encontrados en la secuencia sql

cursor.executemany(sql, seq_of_parameters)
  • Este atributo de sólo lectura que devuelve el número total de filas de la base de datos que han sido modificadas, insertadas o eliminadas por la última ejecución

cursor.rowcount
  • Este método consigna la transacción actual. Si no se llama a este método, cualquier cosa que se haya hecho desde la última llamada a commit() no es visible desde otras conexiones de la base de datos

connection.commit()
  • Este método revierte cualquier cambio en la base de datos desde la última llamada a commit()

connection.rollback()
  • Este método cierra la conexión a la base de datos. Tenga en cuenta que esto no llama automáticamente a commit(). Si simplemente cierra la conexión a la base de datos sin llamar primero a commit(), ¡los cambios se perderán!

connection.close()
  • Este método obtiene la siguiente fila de un conjunto de resultados de consulta, devolviendo una única secuencia, o None cuando no hay más datos disponibles

cursor.fetchone()
  • Esta rutina obtiene el siguiente conjunto de filas de un resultado de consulta, devolviendo una lista. Se devuelve una lista vacía cuando no hay más filas disponibles. El método intenta obtener tantas filas como indique el parámetro de tamaño.

cursor.fetchmany([size=cursor.arraysize])
  • Esta rutina recupera todas las filas (restantes) de un resultado de consulta, devolviendo una lista. Se devuelve una lista vacía cuando no hay filas disponibles.

cursor.fetchall()

Conexión a la base de datos

  • El siguiente código de Python muestra cómo conectarse a una base de datos existente. Para conectar la base de datos PostgreSQL y realizar consultas SQL, debes conocer el nombre de la base de datos a la que te quieres conectar. EN éste caso la información de la base de datos creada la encontramos en Heroku Postgres. Importamos primero las librerías necesarias para usar psycopg2

import psycopg2
from psycopg2 import Error
try:
    connection = psycopg2.connect(user="xoihbshcjgwwtd",
                                  password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
                                  host="ec2-34-205-14-168.compute-1.amazonaws.com",
                                  port="5432",
                                  database="d4oenn2g04nlqm")

    cursor = connection.cursor()
    print("PostgreSQL server information")
    print(connection.get_dsn_parameters(), "\n")

    cursor.execute("SELECT version();")
    record = cursor.fetchone()
    print("You are connected to - ", record, "\n")

except (Exception, Error) as error:
    print("Error while connecting to PostgreSQL", error)
finally:
    if (connection):
        cursor.close()
        connection.close()
        print("PostgreSQL connection is closed")
PostgreSQL server information
{'user': 'xoihbshcjgwwtd', 'dbname': 'd4oenn2g04nlqm', 'host': 'ec2-34-205-14-168.compute-1.amazonaws.com', 'port': '5432', 'tty': '', 'options': '', 'sslmode': 'prefer', 'sslcompression': '0', 'krbsrvname': 'postgres', 'target_session_attrs': 'any'} 

You are connected to -  ('PostgreSQL 13.4 (Ubuntu 13.4-4.pgdg20.04+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, 64-bit',) 

PostgreSQL connection is closed

Crear una tabla

  • El siguiente programa de Python se utilizará para crear una tabla en la base de datos previamente creada

import psycopg2

connection = psycopg2.connect(user="xoihbshcjgwwtd",
                              password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
                              host="ec2-34-205-14-168.compute-1.amazonaws.com",
                              port="5432",
                              database="d4oenn2g04nlqm")
cursor = connection.cursor()

cursor.execute('''DROP TABLE IF EXISTS company''')
cursor.execute('''CREATE TABLE company(
id SERIAL PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
age INT NOT NULL,
address CHAR(50),
salary REAL);
''')
print("Table created successfully")

connection.commit()
connection.close()
Table created successfully
  • Nótese en pgAdmin que la tabla fué creada exitosamente. También puede visualizar los cambios ocurridos en Heroku Postgres. Para visualizar la tabla en pgAdmin debe hacer click en el nombre de la tabla, luego en el boton con simbolo de tabla que aparece en la parte superiro izquierda.

Operación INSERT

  • El siguiente programa de Python muestra cómo podemos crear registros en nuestra tabla company creada en el ejemplo anterior

import psycopg2

connection = psycopg2.connect(user="xoihbshcjgwwtd",
                              password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
                              host="ec2-34-205-14-168.compute-1.amazonaws.com",
                              port="5432",
                              database="d4oenn2g04nlqm")
cursor = connection.cursor()

cursor.execute("INSERT INTO company (name, age, address, salary) VALUES ('Paul', 32, 'California', 20000.00 )");

cursor.execute("INSERT INTO company (name, age, address, salary) VALUES ('Allen', 25, 'Texas', 15000.00 )");

cursor.execute("INSERT INTO company (name, age, address, salary) VALUES ('Teddy', 23, 'Norway', 20000.00 )");

cursor.execute("INSERT INTO company (name, age, address, salary) \
                VALUES ('Mark', 25, 'Rich-Mond ', 65000.00 )");

connection.commit()
print("Records created successfully")
connection.close()
Records created successfully
  • Observe desde pgAdmin que los registros han sido actualizados efectivamente, con los valores insertados en la tabla creada

Operación SELECT

  • El siguiente programa de Python muestra cómo podemos obtener y mostrar registros de nuestra tabla company creada en el ejemplo anterior

import psycopg2

connection = psycopg2.connect(user="xoihbshcjgwwtd",
                              password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
                              host="ec2-34-205-14-168.compute-1.amazonaws.com",
                              port="5432",
                              database="d4oenn2g04nlqm")
cursor = connection.cursor()

cursor.execute("SELECT id, name, address, salary from company")
rows = cursor.fetchall()

for row in rows:
    print("id = ", row[0])
    print("name = ", row[1])
    print("address = ", row[2])
    print("salary = ", row[3], "\n")

print("Operation done successfully")
connection.close()
id =  1
name =  Paul
address =  California                                        
salary =  20000.0 

id =  2
name =  Allen
address =  Texas                                             
salary =  15000.0 

id =  3
name =  Teddy
address =  Norway                                            
salary =  20000.0 

id =  4
name =  Mark
address =  Rich-Mond                                         
salary =  65000.0 

Operation done successfully

Operación UPDATE

  • El siguiente código de Python muestra cómo podemos utilizar la sentencia UPDATE para actualizar cualquier registro y luego obtener y mostrar los registros actualizados de nuestra tabla company

import psycopg2

connection = psycopg2.connect(user="xoihbshcjgwwtd",
                              password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
                              host="ec2-34-205-14-168.compute-1.amazonaws.com",
                              port="5432",
                              database="d4oenn2g04nlqm")
cursor = connection.cursor()

cursor.execute("UPDATE company set salary = 25000.00 where id = 1")
connection.commit()
print("Total number of rows updated :", cursor.rowcount)

cursor.execute("SELECT id, name, address, salary from company")
rows = cursor.fetchall()

for row in rows:
    print("id = ", row[0])
    print("name = ", row[1])
    print("address = ", row[2])
    print("salary = ", row[3], "\n")

print("Operation done successfully")
connection.close()
Total number of rows updated : 1
id =  2
name =  Allen
address =  Texas                                             
salary =  15000.0 

id =  3
name =  Teddy
address =  Norway                                            
salary =  20000.0 

id =  4
name =  Mark
address =  Rich-Mond                                         
salary =  65000.0 

id =  1
name =  Paul
address =  California                                        
salary =  25000.0 

Operation done successfully

Operación DELETE

  • El siguiente código de Python muestra cómo podemos utilizar la sentencia DELETE para eliminar cualquier registro y luego obtener y mostrar los registros restantes de nuestra tabla company

import psycopg2

connection = psycopg2.connect(user="xoihbshcjgwwtd",
                              password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
                              host="ec2-34-205-14-168.compute-1.amazonaws.com",
                              port="5432",
                              database="d4oenn2g04nlqm")
cursor = connection.cursor()

cursor.execute("DELETE from company where id = 2;")
connection.commit()
print("Total number of rows deleted :", cursor.rowcount)

cursor.execute("SELECT id, name, address, salary from company")
rows = cursor.fetchall()

for row in rows:
    print("id = ", row[0])
    print("name = ", row[1])
    print("address = ", row[2])
    print("salary = ", row[3], "\n")

print("Operation done successfully")
connection.close()
Total number of rows deleted : 1
id =  3
name =  Teddy
address =  Norway                                            
salary =  20000.0 

id =  4
name =  Mark
address =  Rich-Mond                                         
salary =  65000.0 

id =  1
name =  Paul
address =  California                                        
salary =  25000.0 

Operation done successfully

Importar un archivo CSV o un pandas DataFrame en PostgreSQL

  • En éste ejemplo vamos a importar un archivo CSV en PostgreSQL utilizando la función to_sql() de la clase sqlalchemy. El DataFrame a usar corresponde al precio de Ethereum en USD. Vamos a importar el archivo en una tabla en nuestra base de datos y luego vamos a utilizar esta tabla para realizar un gráfico de candlesticks. Si desea ver los distinto tipos de variables numéricas y formatos de fechas en PostgreSQL ver el siguiente par de links: Formatos numéricos, Formatos fechas

df_ETH = pd.read_csv("https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/ETH-USD.csv")
df_ETH.head()
Date Open High Low Close Adj Close Volume
0 2015-08-07 2.831620 3.536610 2.521120 2.772120 2.772120 164329.0
1 2015-08-08 2.793760 2.798810 0.714725 0.753325 0.753325 674188.0
2 2015-08-09 0.706136 0.879810 0.629191 0.701897 0.701897 532170.0
3 2015-08-10 0.713989 0.729854 0.636546 0.708448 0.708448 405283.0
4 2015-08-11 0.708087 1.131410 0.663235 1.067860 1.067860 1463100.0
# from sqlalchemy import create_engine
# engine = create_engine('postgresql://xoihbshcjgwwtd:aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9@ec2-34-205-14-168.compute-1.amazonaws.com:5432/d4oenn2g04nlqm')
# df_ETH.to_sql('ethereum', engine, if_exists = 'replace', index=False)
# import psycopg2

# connection = psycopg2.connect(user="xoihbshcjgwwtd",
#                               password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
#                               host="ec2-34-205-14-168.compute-1.amazonaws.com",
#                               port="5432",
#                               database="d4oenn2g04nlqm")
# cursor = connection.cursor()
# cursor.execute("SELECT * from ethereum;")
# close = cursor.fetchall()
# close_df = pd.DataFrame(close)

# print("Operation done successfully")
# connection.close()
# close_df.columns = ["Date", "Open", "High", "Low", "Close", "Adj_Close", "Volume"]
# close_df = close_df.sort_values(by = "Date")
# close_df.head()
# import plotly.graph_objects as go

# fig = go.Figure(data=[go.Candlestick(x = close_df.date,
#                                      open = close_df.open, 
#                                      high = close_df.high,
#                                      low = close_df.low, 
#                                      close = close_df.close)
#                      ])
# fig.update_layout(
#     title="Ethereum USD (ETH-USD)",
#     xaxis_title="Day",
#     yaxis_title="ETH-USD",
#     font=dict(
#         family="Courier New, monospace",
#         size=12,
#         color="RebeccaPurple"
#     )
# )
# fig.update_layout(xaxis_rangeslider_visible=False)
# fig.show()

Tarea 2.3

  • La primera tarea es poner a funcionar todo lo realziado en esta sección y entenderlo. Luego de esto creará una nueva tabla que llamaremos links, la cual contendrá información acerca de link de páginas web. La tabla debe ser creada siguiendo las siguientes instrucciones. En la siguiente revisaremos con detalle el significado de cada consulta SQL

CREATE TABLE links (
	id SERIAL PRIMARY KEY,
	url VARCHAR(255) NOT NULL,
	name VARCHAR(255) NOT NULL,
	description VARCHAR (255),
    last_update DATE
);
  • Luego insertará datos a la tabla usando las siguientes ordenes. En cada caso mostrar debe mostrar el output generado por pgAdmin, así como también la actualización de Heroku Postgres

INSERT INTO links (url, name)
VALUES('https://www.postgresqltutorial.com','PostgreSQL Tutorial');
INSERT INTO links (url, name)
VALUES('http://www.oreilly.com','O''Reilly Media');
INSERT INTO links (url, name)
VALUES('http://www.oreilly.com','O''Reilly Media');
INSERT INTO links (url, name, last_update)
VALUES('https://www.google.com','Google','2013-06-01');
INSERT INTO links (url, name)
VALUES('http://www.postgresql.org','PostgreSQL') 
RETURNING id;
  • Importe los datos del precio de Cardano USD (ADA-USD) en su base de datos Herokus Postgres, teniendo en cuenta lo explicado. Luego dibuje un gráfico de candlestick para la criptomoneda. En el siguiente link encontrará el CSV de Cardano: Cardano USD (ADA-USD)